From 920be3ee05e3ca596446a4e1703c5a4c5679133c Mon Sep 17 00:00:00 2001 From: CJ Green <44074998+okaycj@users.noreply.github.com> Date: Tue, 7 May 2024 11:06:59 -0400 Subject: [PATCH] Jsdoc Comments (#26) * Update eslint package to fix warning message * Add exit survey pacakge * exit survey * se survey_function to format survey text * edit exit survey question titles and descriptions * change question names and MC values to match EFP names * add withdrawal and feedback questions * Change package name, exit survey parameters * Added privacy and databrary params * Get study to add to withdraw copu * disable the video sharing questions if video withdrawal is true * Add MD link * Add global data store * fix withdrawal question value in data and logic for enabling/disabling the other video-related questions * add validation for child birthdate: cannot be a future date * Update root package * Update survey to use new data structure * First pass at tests * Add data finish function * Update tests * Update versions of deps * freeze loaded data * Add s3 from EFP * Add s3 class * Update linters to support jsdoc comments * First pass at code documentation * Change functions to arrow functions * Fix tests * fix typos * minor edits to JSdoc comments * Format and comment changes * Test updates * Moved user function to run first * A few rebase errors --------- Co-authored-by: Becky Gilbert --- .prettierrc | 3 +- eslint.config.mjs | 30 +- package-lock.json | 1292 ++++++++++++++--- package.json | 4 +- packages/data/src/api.ts | 74 +- packages/data/src/index.ts | 10 +- packages/data/src/s3.ts | 51 +- packages/data/src/types.ts | 5 +- packages/data/src/utils.spec.ts | 15 +- packages/data/src/utils.ts | 59 +- packages/lookit-initjspsych/src/index.ts | 20 +- packages/lookit-initjspsych/src/utils.spec.ts | 28 +- packages/lookit-initjspsych/src/utils.ts | 57 +- packages/surveys/src/consent.ts | 12 +- packages/surveys/src/exit.json | 66 + packages/surveys/src/exit.ts | 56 +- packages/surveys/src/utils.spec.ts | 22 +- packages/surveys/src/utils.ts | 49 +- packages/video/src/index.spec.ts | 2 +- 19 files changed, 1494 insertions(+), 361 deletions(-) create mode 100644 packages/surveys/src/exit.json diff --git a/.prettierrc b/.prettierrc index bb277fa3..80c4dc14 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,6 +3,7 @@ "plugins": [ "prettier-plugin-packagejson", "prettier-plugin-sort-json", - "prettier-plugin-organize-imports" + "prettier-plugin-organize-imports", + "prettier-plugin-jsdoc" ] } diff --git a/eslint.config.mjs b/eslint.config.mjs index 53503e5f..dcaca94b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,4 +1,6 @@ import pluginJs from "@eslint/js"; +import prettierConfig from "eslint-config-prettier"; +import jsdoc from "eslint-plugin-jsdoc"; import globals from "globals"; import tseslint from "typescript-eslint"; @@ -6,11 +8,37 @@ export default [ { languageOptions: { globals: globals.browser } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, + jsdoc.configs["flat/recommended-typescript-error"], { rules: { "require-await": "error", "@typescript-eslint/explicit-member-accessibility": "error", + "prefer-arrow-callback": ["error", { allowNamedFunctions: true }], + "func-style": ["error", "expression", { allowArrowFunctions: true }], }, - ignores: ["packages/data/src/s3/**"], }, + { + plugins: { + jsdoc, + }, + rules: { + "jsdoc/tag-lines": "off", + "jsdoc/require-description-complete-sentence": "error", + "jsdoc/require-description": "error", + "jsdoc/require-hyphen-before-param-description": "error", + "jsdoc/require-jsdoc": [ + "error", + { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + ArrowFunctionExpression: true, + FunctionExpression: true, + }, + }, + ], + }, + }, + prettierConfig, ]; diff --git a/package-lock.json b/package-lock.json index bb4181bf..f18fce7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,10 @@ "@eslint/js": "^9.1.1", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jsdoc": "^48.2.3", "globals": "^15.0.0", "prettier": "^3.2.5", + "prettier-plugin-jsdoc": "^1.3.0", "prettier-plugin-organize-imports": "^3.2.4", "prettier-plugin-packagejson": "^2.5.0", "prettier-plugin-sort-json": "^4.0.0", @@ -742,13 +744,13 @@ } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.568.0", + "version": "3.535.0", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=14.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { @@ -2888,6 +2890,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.42.0", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -2963,10 +2978,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.2.0.tgz", - "integrity": "sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==", + "version": "9.1.1", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -4877,6 +4891,15 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/dompurify": { "version": "3.0.5", "dev": true, @@ -4966,15 +4989,29 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } }, "node_modules/@types/minimist": { "version": "1.2.5", "dev": true, "license": "MIT" }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "node_modules/@types/node": { "version": "12.20.55", "dev": true, @@ -5038,6 +5075,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, "node_modules/@types/vinyl": { "version": "2.0.11", "dev": true, @@ -5071,16 +5114,15 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", - "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", + "version": "7.7.1", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/type-utils": "7.8.0", - "@typescript-eslint/utils": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/type-utils": "7.7.1", + "@typescript-eslint/utils": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.3.1", @@ -5106,15 +5148,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", + "version": "7.7.1", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/typescript-estree": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", "debug": "^4.3.4" }, "engines": { @@ -5134,13 +5175,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", - "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "version": "7.7.1", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0" + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5151,13 +5191,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", - "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", + "version": "7.7.1", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/typescript-estree": "7.7.1", + "@typescript-eslint/utils": "7.7.1", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -5178,10 +5217,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", - "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "version": "7.7.1", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -5191,13 +5229,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", - "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "version": "7.7.1", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5220,9 +5257,8 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5234,17 +5270,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", - "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "version": "7.7.1", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.15", "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/typescript-estree": "7.7.1", "semver": "^7.6.0" }, "engines": { @@ -5259,12 +5294,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", - "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "version": "7.7.1", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/types": "7.7.1", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -5481,6 +5515,14 @@ "dev": true, "license": "MIT" }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/are-we-there-yet": { "version": "2.0.0", "dev": true, @@ -6353,6 +6395,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/binary-searching": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/binary-searching/-/binary-searching-2.0.5.tgz", + "integrity": "sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA==", + "dev": true + }, "node_modules/binaryextensions": { "version": "2.3.0", "dev": true, @@ -6601,6 +6649,16 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "0.7.0", "dev": true, @@ -6886,6 +6944,14 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "dev": true, @@ -7278,6 +7344,19 @@ "dev": true, "license": "MIT" }, + "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", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decode-uri-component": { "version": "0.2.2", "dev": true, @@ -7426,6 +7505,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-file": { "version": "1.0.0", "dev": true, @@ -7461,6 +7549,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "dev": true, @@ -7510,10 +7611,6 @@ "node": ">=12" } }, - "node_modules/dompurify": { - "version": "3.1.2", - "license": "(MPL-2.0 OR Apache-2.0)" - }, "node_modules/duplexify": { "version": "3.7.1", "dev": true, @@ -7891,6 +7988,48 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "48.2.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.42.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "dev": true, @@ -9493,10 +9632,9 @@ } }, "node_modules/globals": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.1.0.tgz", - "integrity": "sha512-926gJqg+4mkxwYKiFvoomM4J0kWESfk3qfTvRL2/oc/tK/eTDBbrfcKnSa2KtfdxB5onoL7D3A3qIHQFpd4+UA==", + "version": "15.0.0", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -12804,6 +12942,14 @@ "signal-exit": "^3.0.2" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsdom": { "version": "20.0.3", "dev": true, @@ -13422,6 +13568,43 @@ "node": ">=0.10.0" } }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/meow": { "version": "6.1.1", "dev": true, @@ -13470,77 +13653,519 @@ "node": ">= 8" } }, - "node_modules/micromatch": { - "version": "4.0.5", + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mime": { - "version": "2.6.0", + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mime-db": { - "version": "1.52.0", + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mimic-response": { + "node_modules/micromark-util-character": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/min-indent": { - "version": "1.0.1", + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/minimatch": { - "version": "3.1.2", + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", "dev": true, "license": "ISC", "dependencies": { @@ -13801,9 +14426,7 @@ } }, "node_modules/npm": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/npm/-/npm-10.7.0.tgz", - "integrity": "sha512-FXylyYSXNjgXx3l82BT8RSQvCoGIQ3h8YdRFGKNvo3Pv/bKscK4pdWkx/onwTpHDqGw+oeLf4Rxln9WVyxAxlQ==", + "version": "10.5.2", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -13821,6 +14444,8 @@ "chalk", "ci-info", "cli-columns", + "cli-table3", + "columnify", "fastest-levenshtein", "fs-minipass", "glob", @@ -13856,6 +14481,7 @@ "npm-profile", "npm-registry-fetch", "npm-user-validate", + "npmlog", "p-map", "pacote", "parse-conflict-json", @@ -13874,6 +14500,7 @@ "which", "write-file-atomic" ], + "license": "Artistic-2.0", "workspaces": [ "docs", "smoke-tests", @@ -13887,10 +14514,10 @@ "@npmcli/config": "^8.0.2", "@npmcli/fs": "^3.1.0", "@npmcli/map-workspaces": "^3.0.6", - "@npmcli/package-json": "^5.1.0", + "@npmcli/package-json": "^5.0.2", "@npmcli/promise-spawn": "^7.0.1", - "@npmcli/redact": "^2.0.0", - "@npmcli/run-script": "^8.1.0", + "@npmcli/redact": "^1.1.0", + "@npmcli/run-script": "^7.0.4", "@sigstore/tuf": "^2.3.2", "abbrev": "^2.0.0", "archy": "~1.0.0", @@ -13898,6 +14525,8 @@ "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-columns": "^4.0.0", + "cli-table3": "^0.6.4", + "columnify": "^1.6.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^10.3.12", @@ -13909,16 +14538,16 @@ "json-parse-even-better-errors": "^3.0.1", "libnpmaccess": "^8.0.1", "libnpmdiff": "^6.0.3", - "libnpmexec": "^8.0.0", + "libnpmexec": "^7.0.4", "libnpmfund": "^5.0.1", "libnpmhook": "^10.0.0", "libnpmorg": "^6.0.1", - "libnpmpack": "^7.0.0", + "libnpmpack": "^6.0.3", "libnpmpublish": "^9.0.2", "libnpmsearch": "^7.0.0", "libnpmteam": "^6.0.0", - "libnpmversion": "^6.0.0", - "make-fetch-happen": "^13.0.1", + "libnpmversion": "^5.0.1", + "make-fetch-happen": "^13.0.0", "minimatch": "^9.0.4", "minipass": "^7.0.4", "minipass-pipeline": "^1.2.4", @@ -13928,15 +14557,16 @@ "normalize-package-data": "^6.0.0", "npm-audit-report": "^5.0.0", "npm-install-checks": "^6.3.0", - "npm-package-arg": "^11.0.2", + "npm-package-arg": "^11.0.1", "npm-pick-manifest": "^9.0.0", - "npm-profile": "^9.0.2", - "npm-registry-fetch": "^17.0.0", + "npm-profile": "^9.0.0", + "npm-registry-fetch": "^16.2.0", "npm-user-validate": "^2.0.0", + "npmlog": "^7.0.1", "p-map": "^4.0.0", - "pacote": "^18.0.3", + "pacote": "^17.0.6", "parse-conflict-json": "^3.0.1", - "proc-log": "^4.2.0", + "proc-log": "^3.0.0", "qrcode-terminal": "^0.12.0", "read": "^3.0.1", "semver": "^7.6.0", @@ -13970,6 +14600,15 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "inBundle": true, @@ -14053,21 +14692,21 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "7.5.1", + "version": "7.4.2", "inBundle": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^3.1.0", - "@npmcli/installed-package-contents": "^2.1.0", + "@npmcli/installed-package-contents": "^2.0.2", "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^7.1.0", + "@npmcli/metavuln-calculator": "^7.0.0", "@npmcli/name-from-folder": "^2.0.0", "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.1.0", + "@npmcli/package-json": "^5.0.0", "@npmcli/query": "^3.1.0", - "@npmcli/redact": "^2.0.0", - "@npmcli/run-script": "^8.1.0", + "@npmcli/redact": "^1.1.0", + "@npmcli/run-script": "^7.0.2", "bin-links": "^4.0.1", "cacache": "^18.0.0", "common-ancestor-path": "^1.0.1", @@ -14077,13 +14716,13 @@ "minimatch": "^9.0.4", "nopt": "^7.0.0", "npm-install-checks": "^6.2.0", - "npm-package-arg": "^11.0.2", + "npm-package-arg": "^11.0.1", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^17.0.0", - "pacote": "^18.0.1", + "npm-registry-fetch": "^16.2.0", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", "parse-conflict-json": "^3.0.0", - "proc-log": "^4.2.0", - "proggy": "^2.0.0", + "proc-log": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "read-package-json-fast": "^3.0.2", @@ -14100,7 +14739,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "8.3.1", + "version": "8.2.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -14108,7 +14747,7 @@ "ci-info": "^4.0.0", "ini": "^4.1.2", "nopt": "^7.0.0", - "proc-log": "^4.2.0", + "proc-log": "^3.0.0", "read-package-json-fast": "^3.0.2", "semver": "^7.3.5", "walk-up-path": "^3.0.1" @@ -14117,6 +14756,31 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/npm/node_modules/@npmcli/fs": { "version": "3.1.0", "inBundle": true, @@ -14129,14 +14793,14 @@ } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "5.0.6", + "version": "5.0.5", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^7.0.0", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", + "proc-log": "^3.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", @@ -14147,7 +14811,7 @@ } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", + "version": "2.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -14155,7 +14819,7 @@ "npm-normalize-package-bin": "^3.0.0" }, "bin": { - "installed-package-contents": "bin/index.js" + "installed-package-contents": "lib/index.js" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -14176,14 +14840,13 @@ } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "7.1.0", + "version": "7.0.0", "inBundle": true, "license": "ISC", "dependencies": { "cacache": "^18.0.0", "json-parse-even-better-errors": "^3.0.0", - "pacote": "^18.0.0", - "proc-log": "^4.1.0", + "pacote": "^17.0.0", "semver": "^7.3.5" }, "engines": { @@ -14207,7 +14870,7 @@ } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "5.1.0", + "version": "5.0.2", "inBundle": true, "license": "ISC", "dependencies": { @@ -14216,7 +14879,7 @@ "hosted-git-info": "^7.0.0", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", - "proc-log": "^4.0.0", + "proc-log": "^3.0.0", "semver": "^7.5.3" }, "engines": { @@ -14246,7 +14909,7 @@ } }, "node_modules/npm/node_modules/@npmcli/redact": { - "version": "2.0.0", + "version": "1.1.0", "inBundle": true, "license": "ISC", "engines": { @@ -14254,7 +14917,7 @@ } }, "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "8.1.0", + "version": "7.0.4", "inBundle": true, "license": "ISC", "dependencies": { @@ -14262,7 +14925,6 @@ "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", - "proc-log": "^4.0.0", "which": "^4.0.0" }, "engines": { @@ -14424,6 +15086,14 @@ "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", "inBundle": true, @@ -14556,6 +15226,28 @@ "node": ">= 10" } }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/npm/node_modules/cmd-shim": { "version": "6.0.2", "inBundle": true, @@ -14580,11 +15272,36 @@ "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", "inBundle": true, "license": "ISC" }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.3", "inBundle": true, @@ -14644,6 +15361,17 @@ "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm/node_modules/diff": { "version": "5.2.0", "inBundle": true, @@ -14731,6 +15459,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/npm/node_modules/gauge": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^4.0.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/glob": { "version": "10.3.12", "inBundle": true, @@ -14757,6 +15503,11 @@ "inBundle": true, "license": "ISC" }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC" + }, "node_modules/npm/node_modules/hasown": { "version": "2.0.2", "inBundle": true, @@ -14884,6 +15635,11 @@ "node": ">= 12" } }, + "node_modules/npm/node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, "node_modules/npm/node_modules/ip-regex": { "version": "5.0.0", "inBundle": true, @@ -14992,29 +15748,30 @@ "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "8.0.5", + "version": "8.0.3", "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^11.0.2", - "npm-registry-fetch": "^17.0.0" + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "6.1.1", + "version": "6.0.9", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", - "@npmcli/installed-package-contents": "^2.1.0", + "@npmcli/disparity-colors": "^3.0.0", + "@npmcli/installed-package-contents": "^2.0.2", "binary-extensions": "^2.3.0", "diff": "^5.1.0", "minimatch": "^9.0.4", - "npm-package-arg": "^11.0.2", - "pacote": "^18.0.1", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4", "tar": "^6.2.1" }, "engines": { @@ -15022,16 +15779,17 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "8.1.0", + "version": "7.0.10", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", - "@npmcli/run-script": "^8.1.0", + "@npmcli/run-script": "^7.0.2", "ci-info": "^4.0.0", - "npm-package-arg": "^11.0.2", - "pacote": "^18.0.1", - "proc-log": "^4.2.0", + "npm-package-arg": "^11.0.1", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "proc-log": "^3.0.0", "read": "^3.0.1", "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", @@ -15042,7 +15800,7 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "5.0.9", + "version": "5.0.7", "inBundle": true, "license": "ISC", "dependencies": { @@ -15053,53 +15811,53 @@ } }, "node_modules/npm/node_modules/libnpmhook": { - "version": "10.0.4", + "version": "10.0.2", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^16.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "6.0.5", + "version": "6.0.3", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^16.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "7.0.1", + "version": "6.0.9", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/arborist": "^7.2.1", - "@npmcli/run-script": "^8.1.0", - "npm-package-arg": "^11.0.2", - "pacote": "^18.0.1" + "@npmcli/run-script": "^7.0.2", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "9.0.7", + "version": "9.0.5", "inBundle": true, "license": "ISC", "dependencies": { "ci-info": "^4.0.0", "normalize-package-data": "^6.0.0", - "npm-package-arg": "^11.0.2", - "npm-registry-fetch": "^17.0.0", - "proc-log": "^4.2.0", + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.2.0", + "proc-log": "^3.0.0", "semver": "^7.3.7", "sigstore": "^2.2.0", "ssri": "^10.0.5" @@ -15109,37 +15867,37 @@ } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "7.0.4", + "version": "7.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^16.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "6.0.4", + "version": "6.0.2", "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^17.0.0" + "npm-registry-fetch": "^16.2.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "6.0.1", + "version": "5.0.2", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.6", - "@npmcli/run-script": "^8.1.0", + "@npmcli/git": "^5.0.3", + "@npmcli/run-script": "^7.0.2", "json-parse-even-better-errors": "^3.0.0", - "proc-log": "^4.2.0", + "proc-log": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -15147,7 +15905,7 @@ } }, "node_modules/npm/node_modules/lru-cache": { - "version": "10.2.2", + "version": "10.2.0", "inBundle": true, "license": "ISC", "engines": { @@ -15155,7 +15913,7 @@ } }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "13.0.1", + "version": "13.0.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -15168,7 +15926,6 @@ "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", - "proc-log": "^4.2.0", "promise-retry": "^2.0.1", "ssri": "^10.0.0" }, @@ -15389,14 +16146,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { - "version": "3.0.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/npm/node_modules/nopt": { "version": "7.2.0", "inBundle": true, @@ -15464,12 +16213,12 @@ } }, "node_modules/npm/node_modules/npm-package-arg": { - "version": "11.0.2", + "version": "11.0.1", "inBundle": true, "license": "ISC", "dependencies": { "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", + "proc-log": "^3.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, @@ -15503,30 +16252,30 @@ } }, "node_modules/npm/node_modules/npm-profile": { - "version": "9.0.2", + "version": "9.0.0", "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^17.0.0", - "proc-log": "^4.0.0" + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "17.0.0", + "version": "16.2.0", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/redact": "^2.0.0", + "@npmcli/redact": "^1.1.0", "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" + "proc-log": "^3.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -15540,6 +16289,20 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/npm/node_modules/npmlog": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", "inBundle": true, @@ -15555,24 +16318,25 @@ } }, "node_modules/npm/node_modules/pacote": { - "version": "18.0.3", + "version": "17.0.6", "inBundle": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/package-json": "^5.1.0", "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^8.0.0", + "@npmcli/run-script": "^7.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^17.0.0", - "proc-log": "^4.0.0", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0", "promise-retry": "^2.0.1", + "read-package-json": "^7.0.0", + "read-package-json-fast": "^3.0.0", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" @@ -15633,15 +16397,7 @@ } }, "node_modules/npm/node_modules/proc-log": { - "version": "4.2.0", - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm/node_modules/proggy": { - "version": "2.0.0", + "version": "3.0.0", "inBundle": true, "license": "ISC", "engines": { @@ -15718,6 +16474,20 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/npm/node_modules/read-package-json": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "3.0.2", "inBundle": true, @@ -15769,6 +16539,11 @@ "node": ">=10" } }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", "inBundle": true, @@ -15887,11 +16662,6 @@ "inBundle": true, "license": "CC0-1.0" }, - "node_modules/npm/node_modules/sprintf-js": { - "version": "1.1.3", - "inBundle": true, - "license": "BSD-3-Clause" - }, "node_modules/npm/node_modules/ssri": { "version": "10.0.5", "inBundle": true, @@ -16102,6 +16872,14 @@ "inBundle": true, "license": "ISC" }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/npm/node_modules/which": { "version": "4.0.0", "inBundle": true, @@ -16124,6 +16902,14 @@ "node": ">=16" } }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", "inBundle": true, @@ -16859,6 +17645,23 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-jsdoc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-jsdoc/-/prettier-plugin-jsdoc-1.3.0.tgz", + "integrity": "sha512-cQm8xIa0fN9ieJFMXACQd6JPycl+8ouOijAqUqu44EF/s4fXL3Wi9sKXuEaodsEWgCN42Xby/bNhqgM1iWx4uw==", + "dev": true, + "dependencies": { + "binary-searching": "^2.0.5", + "comment-parser": "^1.4.0", + "mdast-util-from-markdown": "^2.0.0" + }, + "engines": { + "node": ">=14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, "node_modules/prettier-plugin-organize-imports": { "version": "3.2.4", "dev": true, @@ -16897,9 +17700,8 @@ }, "node_modules/prettier-plugin-sort-json": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-sort-json/-/prettier-plugin-sort-json-4.0.0.tgz", - "integrity": "sha512-zV5g+bWFD2zAqyQ8gCkwUTC49o9FxslaUdirwivt5GZHcf57hCocavykuyYqbExoEsuBOg8IU36OY7zmVEMOWA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.0.0" }, @@ -18747,22 +19549,17 @@ } }, "node_modules/survey-core": { - "version": "1.10.2", + "version": "1.10.3", "license": "MIT" }, - "node_modules/survey-jquery": { - "version": "1.10.2", - "license": "MIT", - "dependencies": { - "jquery": ">=1.12.4" - } - }, "node_modules/survey-knockout-ui": { - "version": "1.10.2", + "version": "1.10.3", "license": "MIT", "dependencies": { - "knockout": "^3.5.0", - "survey-core": "1.10.2" + "knockout": "^3.5.0" + }, + "peerDependencies": { + "survey-core": "1.10.3" } }, "node_modules/sver-compat": { @@ -19371,14 +20168,13 @@ } }, "node_modules/typescript-eslint": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.8.0.tgz", - "integrity": "sha512-sheFG+/D8N/L7gC3WT0Q8sB97Nm573Yfr+vZFzl/4nBdYcmviBPtwGSX9TJ7wpVg28ocerKVOt+k2eGmHzcgVA==", + "version": "7.7.1", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "7.8.0", - "@typescript-eslint/parser": "7.8.0", - "@typescript-eslint/utils": "7.8.0" + "@typescript-eslint/eslint-plugin": "7.7.1", + "@typescript-eslint/parser": "7.7.1", + "@typescript-eslint/utils": "7.7.1" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -19518,6 +20314,19 @@ "through2-filter": "^3.0.0" } }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "0.1.2", "dev": true, @@ -20212,6 +21021,17 @@ "jspsych": "^7.3.4" } }, + "packages/surveys/node_modules/dompurify": { + "version": "3.0.11", + "license": "(MPL-2.0 OR Apache-2.0)" + }, + "packages/surveys/node_modules/survey-jquery": { + "version": "1.9.136", + "license": "MIT", + "dependencies": { + "jquery": ">=1.12.4" + } + }, "packages/video": { "name": "@lookit/video", "version": "0.0.1", diff --git a/package.json b/package.json index e0cd9253..28494356 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "clean": "find '.' -type d -name 'node_modules' -or -name 'coverage' -or -name 'dist' -exec rm -rf {} \\;", "fix": "prettier './' -lw && npm run lint", "format": "prettier './' -c", - "lint": "eslint './packages/**/src/**/*.ts'", + "lint": "eslint './packages/**/src/**/*.ts' --fix", "test": "npm test --workspaces" }, "dependencies": { @@ -33,8 +33,10 @@ "@eslint/js": "^9.1.1", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jsdoc": "^48.2.3", "globals": "^15.0.0", "prettier": "^3.2.5", + "prettier-plugin-jsdoc": "^1.3.0", "prettier-plugin-organize-imports": "^3.2.4", "prettier-plugin-packagejson": "^2.5.0", "prettier-plugin-sort-json": "^4.0.0", diff --git a/packages/data/src/api.ts b/packages/data/src/api.ts index ec7404e2..e603b3be 100644 --- a/packages/data/src/api.ts +++ b/packages/data/src/api.ts @@ -1,7 +1,7 @@ import { + ApiPromise, Child, PastSession, - Promises, Response, ResponseAttrsUpdate, ResponseUpdate, @@ -10,34 +10,76 @@ import { import { get, getUuids, patch } from "./utils"; const CONFIG = { ...getUuids() }; -const promises: Promises[] = []; +const promises: ApiPromise[] = []; -function deposit(promise: T) { +/** + * Collect all API call promises into an array to be checked with finish(). + * + * @param promise - Promise generated by API calls. + * @returns Provided promise. + */ +const deposit = (promise: T) => { promises.push(promise); return promise; -} +}; -export function finish() { +/** + * Checks to see if all API calls have completed. + * + * @returns A Promise that, if/when resolved, confirms that all API calls have + * completed. + */ +export const finish = () => { return Promise.all(promises); -} +}; -export function retrieveChild() { +/** + * Gets the Child object for this study session from the lookit-api. + * + * @returns API Promise containing the Child object. + */ +export const retrieveChild = () => { return deposit(get(`children/${CONFIG.child}/`)); -} +}; -export function retrievePastSessions(uuid: string) { +/** + * Gets a list of all past Session (response) objects for this Study and Child + * from the lookit-api. + * + * @param uuid - Current Response UUID. + * @returns Promise containing list of all Past Session objects. + */ +export const retrievePastSessions = (uuid: string) => { return deposit(get(`past-sessions/${uuid}/`)); -} +}; -export function retrieveStudy() { +/** + * Gets the Study object from the lookit-api. + * + * @returns API Promise containing the Study object. + */ +export const retrieveStudy = () => { return deposit(get(`studies/${CONFIG.study}/`)); -} +}; -export function retrieveResponse(uuid: string) { +/** + * Gets Response (Session) for the provided response UUID. + * + * @param uuid - Response UUID. + * @returns Promise containing the Response object. + */ +export const retrieveResponse = (uuid: string) => { return deposit(get(`responses/${uuid}/`)); -} +}; -export function updateResponse(uuid: string, data: ResponseAttrsUpdate) { +/** + * Updates an existing Response object in the lookit-api. + * + * @param uuid - Response UUID. + * @param data - Object containing fields to be updated. + * @returns Promise containing the updated Response object. + */ +export const updateResponse = (uuid: string, data: ResponseAttrsUpdate) => { return deposit( patch(`responses/${uuid}/`, { id: uuid, @@ -45,4 +87,4 @@ export function updateResponse(uuid: string, data: ResponseAttrsUpdate) { attributes: data, }), ); -} +}; diff --git a/packages/data/src/index.ts b/packages/data/src/index.ts index 723f81ef..5361750b 100644 --- a/packages/data/src/index.ts +++ b/packages/data/src/index.ts @@ -22,7 +22,13 @@ declare global { } } -async function load(response_uuid: string) { +/** + * Load data from API that is needed for saving the experiment data, and that + * might be needed by researchers and jsPsych. + * + * @param response_uuid - Response UUID. + */ +const load = async (response_uuid: string) => { if (!window.chs) { Object.assign(window, { chs: { @@ -35,6 +41,6 @@ async function load(response_uuid: string) { deepFreeze(window.chs); await finish(); } -} +}; export default { load, retrieveResponse, updateResponse, finish, s3 }; diff --git a/packages/data/src/s3.ts b/packages/data/src/s3.ts index 459b8e8b..5473ea9d 100644 --- a/packages/data/src/s3.ts +++ b/packages/data/src/s3.ts @@ -1,6 +1,7 @@ import { S3 } from "@aws-sdk/client-s3"; import { Env } from "./types"; +/** Provides functionality to upload videos incremetally to an AWS S3 Bucket. */ export default class { private blobParts: Blob[]; private promises: Promise<{ PartNumber: number; ETag?: string }>[]; @@ -11,6 +12,12 @@ export default class { private env: Env; private key: string; + /** + * Provide file name and AWS secrets to upload file to a S3 bucket. + * + * @param key - Used to identify upload, mostly likely video file name. + * @param s3vars - Object with secrets and bucket name. + */ public constructor(key: string, s3vars: Env) { this.env = s3vars; this.key = key; @@ -27,16 +34,30 @@ export default class { }); } + /** + * Calculate the current blob size from the list of blob parts. + * + * @returns Size of collected blobs. + */ private get blobPartsSize() { return this.blobParts.reduce((p, c) => { return p + c.size; }, 0); } + /** + * Current upload percent completed. + * + * @returns Percent uploaded. + */ public get percentUploadComplete() { return Math.floor((this.partsUploaded / this.partNumber) * 100); } + /** + * Create upload part from list of blob parts and add upload promise to list + * of promises. + */ private addUploadPartPromise() { this.promises.push( this.uploadPart(new Blob(this.blobParts), this.partNumber++), @@ -44,6 +65,7 @@ export default class { this.blobParts = []; } + /** Create a AWS S3 upload. */ public async createUpload() { this.logRecordingEvent(`Creating video upload connection.`); const createResponse = await this.s3.createMultipartUpload({ @@ -55,7 +77,15 @@ export default class { this.logRecordingEvent(`Connection established.`); } - public async uploadPart(blob: Blob, partNumber: number) { + /** + * Upload a blob as a part of a whole video. This will retry the partial + * upload a few times before returning an error. + * + * @param blob - Blob representing a part of a whole video. + * @param partNumber - Index of upload part. + * @returns Object containing part number and etag needed by S3. + */ + private async uploadPart(blob: Blob, partNumber: number) { let retry = 0; let err; @@ -91,11 +121,15 @@ export default class { throw Error(`Upload part failed after 3 attempts.\nError: ${err}`); } + /** + * Finalize AWS S3 upload. This is called when all video parts have been + * uploaded. + */ public async completeUpload() { this.addUploadPartPromise(); if (!this.uploadId) { - throw Error("no upload id"); + throw Error("No upload id"); } const resp = await this.s3.completeMultipartUpload({ @@ -110,6 +144,12 @@ export default class { this.logRecordingEvent(`Upload complete: ${resp.Location}`); } + /** + * This will take provided blob and add it to the list of blob parts. If the + * list of blob parts is around 5mb, it will start another partial upload. + * + * @param blob - Part of a video file. + */ public onDataAvailable(blob: Blob) { this.blobParts.push(blob); @@ -118,8 +158,13 @@ export default class { } } + /** + * Log messages to JS console. Right now this just prints to the console, but + * we could also send this info to permanent storage (similar to pipe logs). + * + * @param msg - Text to logged. + */ public logRecordingEvent(msg: string) { - // right now this just prints to the console, but we could also send this info to permanent storage (similar to pipe logs) const timestamp = new Date().toISOString(); console.log(`Recording log: ${timestamp}\nFile: ${this.key}\n${msg}\n`); } diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts index 6374629b..6f900d02 100644 --- a/packages/data/src/types.ts +++ b/packages/data/src/types.ts @@ -1,6 +1,6 @@ import { DataCollection } from "jspsych/dist/modules/data/DataCollection"; -export type Promises = Promise | Data[]>; +export type ApiPromise = Promise | Data[]>; export type Relationship = { links: { @@ -22,15 +22,12 @@ export interface StudyAttrs extends Attributes { criteria: string; duration: string; contact_info: string; - /** Format: binary */ image?: string | null; structure?: Record; generator?: string; use_generator?: boolean; display_full_screen?: boolean; - /** Format: uri */ exit_url?: string; - /** @enum {string} */ state?: | "created" | "submitted" diff --git a/packages/data/src/utils.spec.ts b/packages/data/src/utils.spec.ts index f71b2d8e..f4da766d 100644 --- a/packages/data/src/utils.spec.ts +++ b/packages/data/src/utils.spec.ts @@ -4,16 +4,23 @@ import { get, getUuids, patch } from "./utils"; enableFetchMocks(); -function setLocationHref(href: string) { +/** + * Helper function for this set of tests. This will update the current URL to + * the value provided in argument href. + * + * @param href - URL to be set. + * @returns Supplied URL. + */ +const setLocationHref = (href: string) => { /** - * Helper function for this set of tests. This will update the current URL to the value - * provided in argument href. + * Helper function for this set of tests. This will update the current URL to + * the value provided in argument href. */ delete global.window.location; global.window = Object.create(window); global.window.location = { href }; return href; -} +}; test("Api get function", async () => { const child = { id: new Date().toString() } as Child; diff --git a/packages/data/src/utils.ts b/packages/data/src/utils.ts index ff81b8e9..3433ddf5 100644 --- a/packages/data/src/utils.ts +++ b/packages/data/src/utils.ts @@ -2,29 +2,41 @@ import { ApiResponse } from "./types"; const CONFIG = { url_base: "/api/v2/" }; -export async function jsonData(request: Request) { +/** + * Get JSON data from a Request. + * + * @param request - Request returned by call to API. + * @returns Data from API call. + */ +export const jsonData = async (request: Request) => { const response = await fetch(request); const json = await (response.json() as Promise>); return json.data; -} - -export function get(url: string) { - /** - * Function for REST get. - */ +}; +/** + * Function for REST get. + * + * @param url - URL to get. + * @returns JSON data from API call. + */ +export const get = (url: string) => { const request = new Request(CONFIG.url_base + url, { method: "GET", mode: "same-origin", }); return jsonData(request); -} +}; -export function patch(url: string, data: T) { - /** - * Function for REST patch. - */ +/** + * Function for REST patch. + * + * @param url - URL to patch. + * @param data - JSON data from API call. + * @returns JSON data from API call. + */ +export const patch = (url: string, data: T) => { const request = new Request(CONFIG.url_base + url, { method: "PATCH", headers: { @@ -36,21 +48,28 @@ export function patch(url: string, data: T) { }); return jsonData(request); -} +}; -export function csrfToken() { - /** - * Function to get csrf token from cookies. - */ +/** + * Function to get csrf token from cookies. + * + * @returns CSRF token. + */ +export const csrfToken = () => { return ( document.cookie .split("; ") .find((row) => row.startsWith("csrftoken=")) ?.split("=")[1] ?? "" ); -} +}; -export function getUuids() { +/** + * Get Study and Child UUID from URL. + * + * @returns Object containing UUIDs. + */ +export const getUuids = () => { const locationHref = window.location.href; const uuids = locationHref.replace("preview/", "").split("/").slice(-3, -1); if (locationHref.includes("studies/j/") && uuids && uuids.length === 2) { @@ -58,4 +77,4 @@ export function getUuids() { } else { throw new Error("URL is different than expected."); } -} +}; diff --git a/packages/lookit-initjspsych/src/index.ts b/packages/lookit-initjspsych/src/index.ts index f724a244..9188044c 100644 --- a/packages/lookit-initjspsych/src/index.ts +++ b/packages/lookit-initjspsych/src/index.ts @@ -2,10 +2,13 @@ import { initJsPsych as origInitJsPsych } from "jspsych"; import { JsPsychOptions } from "./types"; import { on_data_update, on_finish } from "./utils"; -function lookitInitJsPsych(responseUuid: string) { - /** - * Function that returns a function to replace jsPsych's initJsPsych. - */ +/** + * Function that returns a function to replace jsPsych's initJsPsych. + * + * @param responseUuid - Response UUID. + * @returns InitJsPsych function. + */ +const lookitInitJsPsych = (responseUuid: string) => { return function (opts: JsPsychOptions) { const jsPsych = origInitJsPsych({ ...opts, @@ -14,6 +17,13 @@ function lookitInitJsPsych(responseUuid: string) { }); const origJsPsychRun = jsPsych.run; + /** + * Overriding default jsPsych run function. This will allow us to + * check/alter the timeline before running an experiment. + * + * @param timeline - List of jsPsych trials. + * @returns Original jsPsych run function. + */ jsPsych.run = function (timeline) { // check timeline here... return origJsPsychRun(timeline); @@ -21,6 +31,6 @@ function lookitInitJsPsych(responseUuid: string) { return jsPsych; }; -} +}; export default lookitInitJsPsych; diff --git a/packages/lookit-initjspsych/src/utils.spec.ts b/packages/lookit-initjspsych/src/utils.spec.ts index 9c75b63e..9f77ac81 100644 --- a/packages/lookit-initjspsych/src/utils.spec.ts +++ b/packages/lookit-initjspsych/src/utils.spec.ts @@ -7,9 +7,14 @@ delete global.window.location; global.window = Object.create(window); global.window.location = { replace: jest.fn() }; -test("jsPysch's on_data_update", async () => { - const jsonData = { data: { attributes: { exp_data: [] } } }; +test("jsPsych's on_data_update with some exp_data", async () => { + const jsonData = { data: { attributes: { exp_data: ["some data"] } } }; const response = { + /** + * Mocked json function used in API calls. + * + * @returns Promise containing mocked json data. + */ json: () => Promise.resolve(jsonData), ok: true, } as Response; @@ -25,9 +30,14 @@ test("jsPysch's on_data_update", async () => { expect(Request).toHaveBeenCalledTimes(2); }); -test("jsPysch's on_data_update", async () => { +test("jsPsych's on_data_update with no exp_data", async () => { const jsonData = { data: { attributes: { exp_data: undefined } } }; const response = { + /** + * Mocked json function used in API calls. + * + * @returns Promise containing mocked json data. + */ json: () => Promise.resolve(jsonData), ok: true, } as Response; @@ -47,8 +57,16 @@ test("jsPsych's on_finish", async () => { const jsonData = { data: { attributes: { exp_data: {} } }, }; - const data = { values: () => {} } as DataCollection; + const data = { + /** Mocked jsPsych Data Collection. */ + values: () => {}, + } as DataCollection; const response = { + /** + * Mocked json function used in API calls. + * + * @returns Promise containing mocked json data. + */ json: () => Promise.resolve(jsonData), ok: true, } as Response; @@ -59,7 +77,7 @@ test("jsPsych's on_finish", async () => { Object.assign(window, { chs: { - study: { attributes: { exit_url: "asdf" } } as Study, + study: { attributes: { exit_url: "exit url" } } as Study, child: {} as Child, pastSessions: {} as PastSession[], }, diff --git a/packages/lookit-initjspsych/src/utils.ts b/packages/lookit-initjspsych/src/utils.ts index fa48e29a..66e42b95 100644 --- a/packages/lookit-initjspsych/src/utils.ts +++ b/packages/lookit-initjspsych/src/utils.ts @@ -2,14 +2,18 @@ import Api from "@lookit/data"; import { DataCollection } from "jspsych/dist/modules/data/DataCollection"; import { UserFunc } from "./types"; -export function on_data_update(responseUuid: string, userFunc?: UserFunc) { - /** - * Function that returns a function to be used in place of jsPsych's option - * "on_data_update". "userFunc" should be the user's implementation of - * "on_data_update". Since this is the data that is returned from each - * trial, this function will get the collected trial data and append the - * current data point. - */ +/** + * Function that returns a function to be used in place of jsPsych's option + * "on_data_update". "userFunc" should be the user's implementation of + * "on_data_update". Since this is the data that is returned from each trial, + * this function will get the collected trial data and append the current data + * point. + * + * @param responseUuid - Response UUID. + * @param userFunc - "on data update" function provided by researcher. + * @returns On data update function. + */ +export const on_data_update = (responseUuid: string, userFunc?: UserFunc) => { return async function (data: DataCollection) { const { attributes } = await Api.retrieveResponse(responseUuid); const exp_data = attributes.exp_data ? attributes.exp_data : []; @@ -23,32 +27,35 @@ export function on_data_update(responseUuid: string, userFunc?: UserFunc) { userFunc(data); } }; -} +}; -export function on_finish(responseUuid: string, userFunc?: UserFunc) { - /** - * Function that returns a function to be used in place of jsPsych's option - * "on_finish". "userFunc" should be the user's implementation of - * "on_finish". Since this is point where the experiment has ended, the - * function will set "completed" to true and overwrites all experiment data - * with the full set of collected data. Once the user function has been - * ran, this will redirect to the study's exit url. - */ +/** + * Function that returns a function to be used in place of jsPsych's option + * "on_finish". "userFunc" should be the user's implementation of "on_finish". + * Since this is point where the experiment has ended, the function will set + * "completed" to true and overwrites all experiment data with the full set of + * collected data. Once the user function has been ran, this will redirect to + * the study's exit url. + * + * @param responseUuid - Response UUID. + * @param userFunc - "on finish" function provided by the researcher. + * @returns On finish function. + */ +export const on_finish = (responseUuid: string, userFunc?: UserFunc) => { return async function (data: DataCollection) { const { exit_url } = window.chs.study.attributes; + // Don't call the function if not defined by user. + if (typeof userFunc === "function") { + userFunc(data); + } + await Api.finish(); await Api.updateResponse(responseUuid, { exp_data: data.values(), completed: true, }); - await Api.finish(); - - // Don't call the function if not defined by user. - if (typeof userFunc === "function") { - userFunc(data); - } exit_url && window.location.replace(exit_url); }; -} +}; diff --git a/packages/surveys/src/consent.ts b/packages/surveys/src/consent.ts index d1a87bfc..8f70755e 100644 --- a/packages/surveys/src/consent.ts +++ b/packages/surveys/src/consent.ts @@ -1,6 +1,6 @@ import SurveyPlugin from "@jspsych/plugin-survey"; import { TrialType } from "jspsych"; -import { consent_survey_function as survey_function } from "./utils"; +import { consentSurveyFunction } from "./utils"; const info = { ...SurveyPlugin.info, @@ -9,12 +9,20 @@ const info = { type Info = typeof info; type Trial = TrialType; +/** Consent Survey plugin extends jsPsych's Survey Plugin. */ export class ConsentSurveyPlugin extends SurveyPlugin { public static readonly info = info; + /** + * Custom consent survey function adds functionality before creating a survey + * based on the user-defined survey JSON/function. + * + * @param display_element - Trial display element. + * @param trial - Info parameters. + */ public trial(display_element: HTMLElement, trial: Trial) { super.trial(display_element, { ...trial, - survey_function: survey_function(trial.survey_function), + survey_function: consentSurveyFunction(trial.survey_function), }); } } diff --git a/packages/surveys/src/exit.json b/packages/surveys/src/exit.json new file mode 100644 index 00000000..ea87f289 --- /dev/null +++ b/packages/surveys/src/exit.json @@ -0,0 +1,66 @@ +{ + "pages": [ + { + "elements": [ + { + "description": "We ask again just to check for typos during registration or accidental selection of a different child at the start of the study.", + "inputType": "date", + "isRequired": true, + "maxValueExpression": "today()", + "name": "birthDate", + "title": "Please confirm your child's birthdate.", + "type": "text" + }, + { + "description": "Only authorized researchers will have access to information in the library. Researchers who are granted access must agree to maintain confidentiality and not use information for commercial purposes. Data sharing will lead to faster progress in research on human development and behavior. If you have any questions about the data-sharing library, please visit [Databrary](https://nyu.databrary.org/) or email ethics@databrary.org.", + "enableIf": "({withdrawal} empty) or ({withdrawal.length} = 0)", + "isRequired": true, + "name": "databraryShare", + "title": "Would you like to share your video and other data from this session with authorized users of the secure data library Databrary?", + "type": "boolean", + "valueFalse": "no", + "valueTrue": "yes" + }, + { + "choices": [ + { + "text": "**Private**: Video may only be viewed by authorized scientists", + "value": "private" + }, + { + "text": "**Scientific and educational**: Video may be shared for scientific or educational purposes. For example, we might show a video clip in a talk at a scientific conference or an undergraduate class about cognitive development, or include an image or video in a scientific paper. In some circumstances, video or images may be available online, for instance as supplemental material in a scientific paper.", + "value": "scientific" + }, + { + "text": "**Publicity**: Please select this option if you'd be excited about seeing your child featured on the Lookit website or in a news article about this study! Your video may be shared for publicity as well as scientific and educational purposes; it will never be used for commercial purposes. Video clips shared may be available online to the general public.", + "value": "public" + } + ], + "description": "", + "enableIf": "({withdrawal} empty) or ({withdrawal.length} = 0)", + "isRequired": true, + "name": "useOfMedia", + "title": "Use of video clips and images:", + "type": "radiogroup" + }, + { + "choices": [], + "defaultValue": [], + "isRequired": false, + "name": "withdrawal", + "title": "Withdrawal of video data", + "type": "checkbox" + }, + { + "autoGrow": true, + "name": "feedback", + "rows": 3, + "title": "How did it go? Do you have any suggestions for improving the study?", + "type": "comment" + } + ], + "name": "page1" + } + ], + "showQuestionNumbers": "off" +} diff --git a/packages/surveys/src/exit.ts b/packages/surveys/src/exit.ts index 7f232022..1c4f6905 100644 --- a/packages/surveys/src/exit.ts +++ b/packages/surveys/src/exit.ts @@ -1,7 +1,7 @@ import SurveyPlugin from "@jspsych/plugin-survey"; import { ParameterType, TrialType } from "jspsych"; import { surveyJSON } from "./exit_json"; -import { exit_survey_function as survey_function } from "./utils"; +import { exitSurveyFunction as survey_function } from "./utils"; const info = { ...SurveyPlugin.info, @@ -33,7 +33,12 @@ const info = { type Info = typeof info; type Trial = TrialType; -function showDatabraryOptions(trial: Trial) { +/** + * Alter survey to show Databrary options. + * + * @param trial - Info parameters. + */ +const showDatabraryOptions = (trial: Trial) => { if (!trial.show_databrary_options) { const survey_elements = surveyJSON.pages[0].elements; const databrary_share_element_idx = survey_elements.findIndex( @@ -41,9 +46,14 @@ function showDatabraryOptions(trial: Trial) { ); survey_elements.splice(databrary_share_element_idx, 1); } -} +}; -function includeWithdrawalExample(trial: Trial) { +/** + * Include parenthetical example in withdrawal language. + * + * @param trial - Info parameters. + */ +const includeWithdrawalExample = (trial: Trial) => { const study = window.chs.study; const withdrawal_element = surveyJSON.pages[0].elements.find( (element) => element.name === "withdrawal", @@ -61,9 +71,14 @@ function includeWithdrawalExample(trial: Trial) { }, ], }); -} +}; -function additionalVideoPrivacyText(trial: Trial) { +/** + * Alter survey to contain additional video privacy text. + * + * @param trial - Info parameters. + */ +const additionalVideoPrivacyText = (trial: Trial) => { const element = surveyJSON.pages[0].elements.find( (element) => element.name === "privacy", ); @@ -71,9 +86,14 @@ function additionalVideoPrivacyText(trial: Trial) { Object.assign(element, { description: trial.additional_video_privacy_text, }); -} +}; -function privateLevelOnly(trial: Trial) { +/** + * Alter survey to only show "private" on use of media question. + * + * @param trial - Info parameters. + */ +const privateLevelOnly = (trial: Trial) => { if (trial.private_level_only) { const media_use_element = surveyJSON.pages[0].elements.find( (element) => element.name === "useOfMedia", @@ -87,9 +107,15 @@ function privateLevelOnly(trial: Trial) { isRequired: false, }); } -} +}; -function surveyParameters(trial: Trial) { +/** + * Process all survey parameter functions. + * + * @param trial - Info parameters. + * @returns Survey JSON. + */ +const surveyParameters = (trial: Trial) => { [ showDatabraryOptions, includeWithdrawalExample, @@ -97,10 +123,18 @@ function surveyParameters(trial: Trial) { privateLevelOnly, ].map((fn) => fn(trial)); return surveyJSON; -} +}; +/** Exit Survey Plugin extending jsPsych's Survey Plugin. */ export class ExitSurveyPlugin extends SurveyPlugin { public static readonly info = info; + /** + * Extended trial method supplied with parameters necessary for our Exit + * Survey. + * + * @param display_element - Display element. + * @param trial - Info parameters. + */ public trial(display_element: HTMLElement, trial: Trial) { super.trial(display_element, { ...trial, diff --git a/packages/surveys/src/utils.spec.ts b/packages/surveys/src/utils.spec.ts index c8e9ed09..7e08189d 100644 --- a/packages/surveys/src/utils.spec.ts +++ b/packages/surveys/src/utils.spec.ts @@ -1,8 +1,8 @@ import { Model } from "survey-jquery"; import { - consent_survey_function, - exit_survey_function, - text_markdown_survey_function, + consentSurveyFunction, + exitSurveyFunction, + textMarkdownSurveyFunction, } from "./utils"; jest.mock("@lookit/data", () => ({ @@ -10,12 +10,12 @@ jest.mock("@lookit/data", () => ({ updateResponse: jest.fn().mockReturnValue("Response"), })); -test("", () => { +test("Markdown to HTML through survey function", () => { const addMock = jest.fn(); const survey = { onTextMarkdown: { add: addMock } } as unknown as Model; const textValue = "some text"; const options = { text: `**${textValue}**`, html: null }; - const rtnSurvey = text_markdown_survey_function(survey); + const rtnSurvey = textMarkdownSurveyFunction(survey); const anonFn = addMock.mock.calls[0][0]; anonFn(null, options); @@ -30,7 +30,7 @@ test("Exit survey function", () => { onComplete: { add: jest.fn() }, onTextMarkdown: { add: jest.fn() }, } as unknown as Model; - const rtnSurvey = exit_survey_function(survey); + const rtnSurvey = exitSurveyFunction(survey); expect(survey.onComplete.add).toHaveBeenCalledTimes(1); expect(survey.onTextMarkdown.add).toHaveBeenCalledTimes(1); @@ -45,7 +45,7 @@ test("Anonymous function within exit survey function where withdrawal > 0", () = } as unknown as Model; const sender = { setValue: jest.fn() }; - exit_survey_function(survey); + exitSurveyFunction(survey); const anonFn = addMock.mock.calls[0][0]; @@ -64,7 +64,7 @@ test("Anonymous function within exit survey function where withdrawal is 0", () } as unknown as Model; const sender = { setValue: jest.fn() }; - exit_survey_function(survey); + exitSurveyFunction(survey); const anonFn = addMock.mock.calls[0][0]; @@ -81,7 +81,7 @@ test("Consent survey function", () => { onComplete: { add: jest.fn() }, onTextMarkdown: { add: jest.fn() }, } as unknown as Model; - const survey_function = consent_survey_function(); + const survey_function = consentSurveyFunction(); const rtnSurvey = survey_function(survey); expect(survey.onComplete.add).toHaveBeenCalledTimes(1); @@ -95,7 +95,7 @@ test("User function for consent survey function", () => { onTextMarkdown: { add: jest.fn() }, } as unknown as Model; const userFn = jest.fn(); - const survey_function = consent_survey_function(userFn); + const survey_function = consentSurveyFunction(userFn); survey_function(survey); @@ -109,7 +109,7 @@ test("Anonymous function within consent survey function", () => { onTextMarkdown: { add: jest.fn() }, } as unknown as Model; - consent_survey_function()(survey); + consentSurveyFunction()(survey); const anonFn = addMock.mock.calls[0][0]; diff --git a/packages/surveys/src/utils.ts b/packages/surveys/src/utils.ts index 615db7ad..34e24cbf 100644 --- a/packages/surveys/src/utils.ts +++ b/packages/surveys/src/utils.ts @@ -8,7 +8,13 @@ const CONFIG = { dompurify: { USE_PROFILES: { html: true } }, }; -export function text_markdown_survey_function(survey: Model) { +/** + * Add markdown transcoding to survey text. + * + * @param survey - Survey model provided by SurveyJS. + * @returns Survey model. + */ +export const textMarkdownSurveyFunction = (survey: Model) => { survey.onTextMarkdown.add((_sender, options) => { // We can set the type as "string" because async is false. options.html = DOMPurify.sanitize( @@ -17,25 +23,42 @@ export function text_markdown_survey_function(survey: Model) { ); }); return survey; -} - -export function exit_survey_function(survey: Model) { - text_markdown_survey_function(survey); - // For the withdrawal checkbox question, this takes the boolean response value out of an array - // and saves it as a single value (since there is always only one checkbox). - // We went with the checkbox question type rather than boolean with "renderAs: checkbox" because the - // latter doesn't allow both a question title and label next to the checkbox. +}; + +/** + * Survey function used in exit survey. Adds markdown support through + * "textMarkdownSurveyFunction". For the withdrawal checkbox question, this + * takes the boolean response value out of an array and saves it as a single + * value (since there is always only one checkbox). We went with the checkbox + * question type rather than boolean with "renderAs: checkbox" because the + * latter doesn't allow both a question title and label next to the checkbox. + * + * @param survey - Survey model provided by SurveyJS. + * @returns Survey model. + */ +export const exitSurveyFunction = (survey: Model) => { + textMarkdownSurveyFunction(survey); + survey.onComplete.add((sender) => { const trueFalseValue = sender.getQuestionByName("withdrawal").value.length > 0; sender.setValue("withdrawal", trueFalseValue); }); return survey; -} +}; -export function consent_survey_function(userfn?: (x: Model) => Model) { +/** + * Survey function used by Consent Survey. Adds markdown support through + * "textMarkdownSurveyFunction". On complete, this will mark in the Response + * that consent was completed, and that the consent was through a survey (rather + * than video). + * + * @param userfn - Survey function provided by user. + * @returns Survey model. + */ +export const consentSurveyFunction = (userfn?: (x: Model) => Model) => { return function (survey: Model) { - text_markdown_survey_function(survey); + textMarkdownSurveyFunction(survey); survey.onComplete.add(async () => { await Data.updateResponse(window.chs.response.id, { @@ -50,4 +73,4 @@ export function consent_survey_function(userfn?: (x: Model) => Model) { return survey; }; -} +}; diff --git a/packages/video/src/index.spec.ts b/packages/video/src/index.spec.ts index e9fdd24b..3a8d9f94 100644 --- a/packages/video/src/index.spec.ts +++ b/packages/video/src/index.spec.ts @@ -1 +1 @@ -test("", () => {}); +test("Place holder function while we develop video plugin", () => {});