diff --git a/package-lock.json b/package-lock.json index 418f41a..26b6988 100644 --- a/package-lock.json +++ b/package-lock.json @@ -134,6 +134,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2091,6 +2092,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2114,6 +2116,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -4143,7 +4146,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4155,7 +4157,6 @@ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -4191,6 +4192,7 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -4262,6 +4264,7 @@ "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/types": "8.47.0", @@ -4618,7 +4621,6 @@ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -4629,24 +4631,21 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", @@ -4654,7 +4653,6 @@ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4666,8 +4664,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", @@ -4675,7 +4672,6 @@ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4689,7 +4685,6 @@ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -4700,7 +4695,6 @@ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -4710,8 +4704,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", @@ -4719,7 +4712,6 @@ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4737,7 +4729,6 @@ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -4752,7 +4743,6 @@ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4766,7 +4756,6 @@ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4782,7 +4771,6 @@ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -4793,16 +4781,14 @@ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/abbrev": { "version": "1.1.1", @@ -4817,6 +4803,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4830,7 +4817,6 @@ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -4891,6 +4877,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5232,6 +5219,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -5480,7 +5468,6 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0" } @@ -5742,6 +5729,7 @@ "integrity": "sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "meow": "^13.0.0" }, @@ -5771,6 +5759,7 @@ "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -5834,6 +5823,7 @@ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -6063,7 +6053,6 @@ "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -6190,6 +6179,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6493,7 +6483,6 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.x" } @@ -6904,8 +6893,7 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/global-directory": { "version": "4.0.1", @@ -7123,7 +7111,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -7477,7 +7465,6 @@ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -7493,7 +7480,6 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7540,6 +7526,7 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -7702,7 +7689,6 @@ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.11.5" }, @@ -8164,8 +8150,7 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", @@ -8210,7 +8195,6 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8221,7 +8205,6 @@ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -9800,6 +9783,7 @@ "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -10041,7 +10025,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/saxes": { @@ -10611,7 +10595,6 @@ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -10696,7 +10679,6 @@ "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -11103,6 +11085,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11321,6 +11304,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11522,7 +11506,6 @@ "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -11547,7 +11530,6 @@ "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -11597,7 +11579,6 @@ "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" } @@ -11608,7 +11589,6 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -11623,7 +11603,6 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 2f45dbe..84996d4 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -25,6 +25,7 @@ import * as dailyAdapter from './daily'; import * as walletAdapter from './wallet'; import { default as cometdAdapter } from './cometd'; import { default as Channel } from './channel'; +import * as powerpointAdapter from './powerpoint'; export { accountAdapter, @@ -54,4 +55,5 @@ export { dailyAdapter, walletAdapter, Channel, + powerpointAdapter, }; diff --git a/src/adapters/powerpoint.ts b/src/adapters/powerpoint.ts new file mode 100644 index 0000000..424da9c --- /dev/null +++ b/src/adapters/powerpoint.ts @@ -0,0 +1,229 @@ +import fetch from 'cross-fetch'; + +import type { RoutingOptions } from '../utils/router'; +import { Router, identification, config } from '../utils'; + +export type TemplateDirectory = 'DATA' | 'MODEL'; + +// ────────────────────────────────────────────── +// Data Points +// ────────────────────────────────────────────── + +export interface NDataPoint { + n?: number | null; +} + +export interface XYDataPoint { + x?: number | null; + y?: number | null; +} + +// ────────────────────────────────────────────── +// Chart Series +// ────────────────────────────────────────────── + +export interface BarSeriesShadow { + objectType: 'bar'; + name?: string; + data?: NDataPoint[] | null; +} + +export interface AreaSeriesShadow { + objectType: 'area'; + name?: string; + data?: NDataPoint[] | null; +} + +export interface LineSeriesShadow { + objectType: 'line'; + name?: string; + data?: NDataPoint[] | null; +} + +export interface PieSeriesShadow { + objectType: 'pie'; + name?: string; + data?: NDataPoint[] | null; +} + +export interface ScatterSeriesShadow { + objectType: 'scatter'; + name?: string; + data?: XYDataPoint[] | null; +} + +export interface YSeriesShadow { + objectType: 'y'; + name?: string; + data?: number[] | null; +} + +export interface XYSeriesShadow { + objectType: 'xy'; + name?: string; + data?: XYDataPoint[] | null; +} + +export type SeriesShadow = + | BarSeriesShadow + | AreaSeriesShadow + | LineSeriesShadow + | PieSeriesShadow + | ScatterSeriesShadow + | YSeriesShadow + | XYSeriesShadow; + +// ────────────────────────────────────────────── +// Chart, Table, Picture +// ────────────────────────────────────────────── + +export interface ChartShadow { + name?: string; + categories?: unknown[] | null; + series?: SeriesShadow[] | null; +} + +export interface TableShadow { + name?: string; + header?: unknown; + data?: unknown[] | null; +} + +export interface PictureShadow { + name?: string; + data?: BinaryData; +} + +// ────────────────────────────────────────────── +// Binary Data +// ────────────────────────────────────────────── + +export interface BinaryData { + encoding: 'HEX' | 'BASE_64'; + data: unknown; + encryption?: 'AES' | null; + name?: string | null; + content_type?: string | null; + contentType?: unknown; +} + +// ────────────────────────────────────────────── +// Environment, Slide, Document +// ────────────────────────────────────────────── + +export interface EnvironmentShadow { + parameters?: Record | null; + charts?: ChartShadow[] | null; + tables?: TableShadow[] | null; + pictures?: PictureShadow[] | null; +} + +export interface SlideShadow { + /** Slide number (1-based) */ + number?: number; + environment?: EnvironmentShadow; +} + +export interface DocumentShadow { + output?: string; + environment?: EnvironmentShadow; + slides?: SlideShadow[] | null; +} + + +/** + * Generates a PowerPoint file from a template and returns it as binary data (JSON-encoded) + * Base URL: PUT `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/powerpoint/{TEMPLATE_DIRECTORY}/{TEMPLATE_PATH}` + * + * @example + * import { powerpointAdapter } from 'epicenter-libs'; + * const binaryData = await powerpointAdapter.generate('MODEL', 'en-US-debrief-template.pptx', { + * output: 'debrief-slides.pptx', + * environment: {}, + * slides: [ + * { + * number: 1, + * environment: { + * tables: [{ name: 'Leaderboard', data: [['Rank', 'Name', 'Score']] }], + * }, + * }, + * ], + * }); + * + * @param templateDirectory Directory where the template is stored: 'DATA' or 'MODEL' + * @param templatePath Path to the template file within the directory + * @param document Document shadow defining the output filename, environment, and slides + * @param [optionals] Optional arguments; pass network call options overrides here. + * @returns promise that resolves to the generated PowerPoint as BinaryData + */ +export async function generate( + templateDirectory: TemplateDirectory, + templatePath: string, + document: DocumentShadow, + optionals: RoutingOptions = {}, +): Promise { + return new Router() + .put(`/powerpoint/${templateDirectory}/${templatePath}`, { + body: document, + ...optionals, + }).then(({ body }) => body); +} + + +/** + * Generates a PowerPoint file from a template and returns it as a streaming response. + * Useful for downloading the generated file directly. + * Base URL: POST `https://forio.com/api/v3/{ACCOUNT}/{PROJECT}/powerpoint/{TEMPLATE_DIRECTORY}/{TEMPLATE_PATH}` + * + * @example + * import { powerpointAdapter } from 'epicenter-libs'; + * const response = await powerpointAdapter.stream('MODEL', 'en-US-debrief-template.pptx', { + * output: 'debrief-slides.pptx', + * environment: {}, + * slides: [], + * }); + * const blob = await response.blob(); + * + * @param templateDirectory Directory where the template is stored: 'DATA' or 'MODEL' + * @param templatePath Path to the template file within the directory + * @param document Document shadow defining the output filename, environment, and slides + * @param [optionals] Optional arguments; pass network call options overrides here. + * @returns promise that resolves to the raw Response for streaming/blob handling + */ +export async function stream( + templateDirectory: TemplateDirectory, + templatePath: string, + document: DocumentShadow, + optionals: RoutingOptions = {}, +): Promise { + const { server, accountShortName, projectShortName, useProjectProxy, query, headers: headersOverride, authorization, includeAuthorization } = optionals; + const url = new Router().getURL(`/powerpoint/${templateDirectory}/${templatePath}`, { + server, + accountShortName, + projectShortName, + useProjectProxy, + query, + }); + + const headers: Record = { + 'Content-type': 'application/json; charset=UTF-8', + ...headersOverride, + }; + + if (includeAuthorization !== false) { + const { session } = identification; + if (!headers.Authorization) { + if (session) headers.Authorization = `Bearer ${session.token}`; + if (authorization) headers.Authorization = authorization; + if (config.authOverride) headers.Authorization = config.authOverride; + } + } + + return fetch(url.toString(), { + method: 'POST', + cache: 'no-cache', + redirect: 'follow', + headers, + body: JSON.stringify(document), + }); +} diff --git a/src/epicenter.ts b/src/epicenter.ts index 1891fc4..9566ec0 100644 --- a/src/epicenter.ts +++ b/src/epicenter.ts @@ -125,6 +125,7 @@ export { walletAdapter, Channel, cometdAdapter, + powerpointAdapter, } from './adapters'; /* APIs */ diff --git a/tests/common.js b/tests/common.js index 01b33a1..2c07317 100644 --- a/tests/common.js +++ b/tests/common.js @@ -208,6 +208,7 @@ export const { videoAdapter, vonageAdapter, cometdAdapter, + powerpointAdapter, } = globalThis.epicenter || {}; export const testedMethods = new Set(); diff --git a/tests/powerpoint.spec.js b/tests/powerpoint.spec.js new file mode 100644 index 0000000..4f69c6b --- /dev/null +++ b/tests/powerpoint.spec.js @@ -0,0 +1,159 @@ +import { + it, + expect, + describe, + afterAll, + beforeAll, + beforeEach, +} from 'vitest'; +import { + ACCOUNT, + PROJECT, + SESSION, + GENERIC_OPTIONS, + createFetchMock, + testedMethods, + getAuthHeader, + powerpointAdapter, + authAdapter, + config, + getFunctionKeys, +} from './common'; + +describe('powerpointAdapter', () => { + let capturedRequests = []; + let mockSetup; + + config.accountShortName = ACCOUNT; + config.projectShortName = PROJECT; + + const TEMPLATE_DIRECTORY = 'MODEL'; + const TEMPLATE_PATH = 'en-US-debrief-template.pptx'; + const DOCUMENT_SHADOW = { + output: 'debrief-slides.pptx', + environment: {}, + slides: [ + { + number: 1, + environment: { + tables: [ + { + name: 'Leaderboard', + data: [['Rank', 'Name', 'Score']], + }, + ], + }, + }, + ], + }; + + beforeAll(() => { + mockSetup = createFetchMock(); + capturedRequests = mockSetup.capturedRequests; + }); + + beforeEach(() => { + capturedRequests.length = 0; + authAdapter.setLocalSession(SESSION); + }); + + afterAll(() => { + mockSetup.restore(); + authAdapter.setLocalSession(undefined); + }); + + describe('powerpointAdapter.generate', () => { + it('Should do a PUT', async () => { + await powerpointAdapter.generate(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(req.options.method.toUpperCase()).toBe('PUT'); + }); + + it('Should have authorization', async () => { + await powerpointAdapter.generate(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(getAuthHeader(req.requestHeaders)).toBe(`Bearer ${SESSION.token}`); + }); + + it('Should use the powerpoint/templateDirectory/templatePath URL', async () => { + await powerpointAdapter.generate(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(req.url).toBe(`https://${config.apiHost}/api/v${config.apiVersion}/${config.accountShortName}/${config.projectShortName}/powerpoint/${TEMPLATE_DIRECTORY}/${TEMPLATE_PATH}`); + }); + + it('Should support generic URL options', async () => { + await powerpointAdapter.generate(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW, GENERIC_OPTIONS); + const req = capturedRequests[capturedRequests.length - 1]; + const { server, accountShortName, projectShortName } = GENERIC_OPTIONS; + expect(req.url).toBe(`${server}/api/v${config.apiVersion}/${accountShortName}/${projectShortName}/powerpoint/${TEMPLATE_DIRECTORY}/${TEMPLATE_PATH}`); + }); + + it('Should send the document shadow in the request body', async () => { + await powerpointAdapter.generate(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + const body = JSON.parse(req.options.body); + expect(body).toEqual(DOCUMENT_SHADOW); + }); + + it('Should support DATA as a template directory', async () => { + await powerpointAdapter.generate('DATA', TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(req.url).toContain('/powerpoint/DATA/'); + }); + + testedMethods.add('generate'); + }); + + describe('powerpointAdapter.stream', () => { + it('Should do a POST', async () => { + await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(req.options.method.toUpperCase()).toBe('POST'); + }); + + it('Should have authorization', async () => { + await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(getAuthHeader(req.requestHeaders)).toBe(`Bearer ${SESSION.token}`); + }); + + it('Should use the powerpoint/templateDirectory/templatePath URL', async () => { + await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + expect(req.url).toBe(`https://${config.apiHost}/api/v${config.apiVersion}/${config.accountShortName}/${config.projectShortName}/powerpoint/${TEMPLATE_DIRECTORY}/${TEMPLATE_PATH}`); + }); + + it('Should support generic URL options', async () => { + await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW, GENERIC_OPTIONS); + const req = capturedRequests[capturedRequests.length - 1]; + const { server, accountShortName, projectShortName } = GENERIC_OPTIONS; + expect(req.url).toBe(`${server}/api/v${config.apiVersion}/${accountShortName}/${projectShortName}/powerpoint/${TEMPLATE_DIRECTORY}/${TEMPLATE_PATH}`); + }); + + it('Should send the document shadow in the request body', async () => { + await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + const req = capturedRequests[capturedRequests.length - 1]; + const body = JSON.parse(req.options.body); + expect(body).toEqual(DOCUMENT_SHADOW); + }); + + it('Should pass custom headers when provided', async () => { + const customHeaders = { 'X-Custom-Header': 'test-value' }; + await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW, { headers: customHeaders }); + const req = capturedRequests[capturedRequests.length - 1]; + expect(req.requestHeaders['X-Custom-Header']).toBe('test-value'); + }); + + it('Should return a raw Response', async () => { + const result = await powerpointAdapter.stream(TEMPLATE_DIRECTORY, TEMPLATE_PATH, DOCUMENT_SHADOW); + expect(result).toBeInstanceOf(Response); + }); + + testedMethods.add('stream'); + }); + + it('Should not have any untested methods', () => { + const actualMethods = getFunctionKeys(powerpointAdapter); + expect(actualMethods).toEqual(testedMethods); + }); +});