@maas/payload-plugin-media-cloud 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +8 -0
  2. package/dist/adapter/handleDelete.d.ts +20 -0
  3. package/dist/adapter/handleDelete.js +70 -0
  4. package/dist/adapter/handleDelete.js.map +1 -0
  5. package/dist/adapter/handleUpload.d.ts +12 -0
  6. package/dist/adapter/handleUpload.js +29 -0
  7. package/dist/adapter/handleUpload.js.map +1 -0
  8. package/dist/adapter/staticHandler.d.ts +17 -0
  9. package/dist/adapter/staticHandler.js +64 -0
  10. package/dist/adapter/staticHandler.js.map +1 -0
  11. package/dist/adapter/storageAdapter.d.ts +23 -0
  12. package/dist/adapter/storageAdapter.js +30 -0
  13. package/dist/adapter/storageAdapter.js.map +1 -0
  14. package/dist/collections/mediaCollection.d.ts +16 -0
  15. package/dist/collections/mediaCollection.js +139 -0
  16. package/dist/collections/mediaCollection.js.map +1 -0
  17. package/dist/components/index.d.ts +4 -0
  18. package/dist/components/index.js +5 -0
  19. package/dist/components/mux-preview/index.d.ts +2 -0
  20. package/dist/components/mux-preview/index.js +3 -0
  21. package/dist/components/mux-preview/mux-preview.d.ts +14 -0
  22. package/dist/components/mux-preview/mux-preview.js +38 -0
  23. package/dist/components/mux-preview/mux-preview.js.map +1 -0
  24. package/dist/components/upload-handler/index.d.ts +2 -0
  25. package/dist/components/upload-handler/index.js +3 -0
  26. package/dist/components/upload-handler/upload-handler.d.ts +22 -0
  27. package/dist/components/upload-handler/upload-handler.js +178 -0
  28. package/dist/components/upload-handler/upload-handler.js.map +1 -0
  29. package/dist/components/upload-manager/index.d.ts +2 -0
  30. package/dist/components/upload-manager/index.js +3 -0
  31. package/dist/components/upload-manager/upload-manager-DN4RrmYB.css +204 -0
  32. package/dist/components/upload-manager/upload-manager-DN4RrmYB.css.map +1 -0
  33. package/dist/components/upload-manager/upload-manager.css +201 -0
  34. package/dist/components/upload-manager/upload-manager.d.ts +42 -0
  35. package/dist/components/upload-manager/upload-manager.js +315 -0
  36. package/dist/components/upload-manager/upload-manager.js.map +1 -0
  37. package/dist/components/upload-manager/upload-manager2.js +0 -0
  38. package/dist/endpoints/muxAssetHandler.d.ts +11 -0
  39. package/dist/endpoints/muxAssetHandler.js +59 -0
  40. package/dist/endpoints/muxAssetHandler.js.map +1 -0
  41. package/dist/endpoints/muxCreateUploadHandler.d.ts +13 -0
  42. package/dist/endpoints/muxCreateUploadHandler.js +40 -0
  43. package/dist/endpoints/muxCreateUploadHandler.js.map +1 -0
  44. package/dist/endpoints/muxWebhookHandler.d.ts +11 -0
  45. package/dist/endpoints/muxWebhookHandler.js +49 -0
  46. package/dist/endpoints/muxWebhookHandler.js.map +1 -0
  47. package/dist/hooks/useEmitter.d.ts +48 -0
  48. package/dist/hooks/useEmitter.js +19 -0
  49. package/dist/hooks/useEmitter.js.map +1 -0
  50. package/dist/hooks/useErrorHandler.d.ts +11 -0
  51. package/dist/hooks/useErrorHandler.js +19 -0
  52. package/dist/hooks/useErrorHandler.js.map +1 -0
  53. package/dist/index.d.ts +3 -0
  54. package/dist/index.js +3 -0
  55. package/dist/plugin.d.ts +15 -0
  56. package/dist/plugin.js +242 -0
  57. package/dist/plugin.js.map +1 -0
  58. package/dist/tus/stores/s3/expiration-manager.d.ts +36 -0
  59. package/dist/tus/stores/s3/expiration-manager.js +76 -0
  60. package/dist/tus/stores/s3/expiration-manager.js.map +1 -0
  61. package/dist/tus/stores/s3/file-operations.d.ts +66 -0
  62. package/dist/tus/stores/s3/file-operations.js +90 -0
  63. package/dist/tus/stores/s3/file-operations.js.map +1 -0
  64. package/dist/tus/stores/s3/log.d.ts +5 -0
  65. package/dist/tus/stores/s3/log.js +8 -0
  66. package/dist/tus/stores/s3/log.js.map +1 -0
  67. package/dist/tus/stores/s3/metadata-manager.d.ts +85 -0
  68. package/dist/tus/stores/s3/metadata-manager.js +135 -0
  69. package/dist/tus/stores/s3/metadata-manager.js.map +1 -0
  70. package/dist/tus/stores/s3/parts-manager.d.ts +130 -0
  71. package/dist/tus/stores/s3/parts-manager.js +328 -0
  72. package/dist/tus/stores/s3/parts-manager.js.map +1 -0
  73. package/dist/tus/stores/s3/s3-store.d.ts +110 -0
  74. package/dist/tus/stores/s3/s3-store.js +342 -0
  75. package/dist/tus/stores/s3/s3-store.js.map +1 -0
  76. package/dist/tus/stores/s3/semaphore.d.ts +16 -0
  77. package/dist/tus/stores/s3/semaphore.js +32 -0
  78. package/dist/tus/stores/s3/semaphore.js.map +1 -0
  79. package/dist/types/errors.d.ts +26 -0
  80. package/dist/types/errors.js +28 -0
  81. package/dist/types/errors.js.map +1 -0
  82. package/dist/types/index.d.ts +73 -0
  83. package/dist/types/index.js +0 -0
  84. package/dist/utils/file.d.ts +30 -0
  85. package/dist/utils/file.js +84 -0
  86. package/dist/utils/file.js.map +1 -0
  87. package/package.json +92 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["export enum MediaCloudError {\n // Mux Errors\n MUX_CONFIG_MISSING = 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n MUX_CONFIG_INCOMPLETE = 'Mux configuration is missing. Mux features will not be available',\n MUX_UPLOAD_ID_MISSING = 'No upload-id found for upload',\n MUX_ASSET_DELETE_ERROR = 'Error deleting Mux asset',\n MUX_UPLOAD_ERROR = 'Mux video upload failed',\n MUX_DIRECT_UPLOAD_ERROR = 'Mux direct upload failed',\n MUX_CREATE_UPLOAD_ERROR = 'Error in Mux create upload handler',\n MUX_REQUEST_NO_JSON = 'Request does not support json() method',\n\n // S3 Errors\n S3_CONFIG_MISSING = 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n S3_DELETE_ERROR = 'Error deleting file from S3',\n S3_UNIQUE_NAME_ERROR = 'Could not find a unique file name after maximum tries',\n\n // TUS Errors\n TUS_UPLOAD_ERROR = 'TUS file upload error occurred',\n\n // File Errors\n FILE_TYPE_UNKNOWN = 'Unable to determine file type',\n FILE_TYPE_ERROR = 'Error determining file type',\n FILENAME_SANITIZE_ERROR = 'Error sanitizing filename',\n\n // Upload Errors\n UPLOAD_NO_URL = 'No upload URL provided, cannot parse upload ID',\n UPLOAD_HANDLER_ERROR = 'Upload handler error occurred',\n UPLOAD_POLLING_ERROR = 'Polling error for upload',\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED = 'Payload Media Cloud plugin is not configured',\n NAMING_FUNCTION_ERROR = 'Error in namingFunction',\n}\n"],"mappings":";AAAA,IAAY,8DAAL;;;;;;;;;;;;;;;;;;;;;;AAgCN"}
@@ -0,0 +1,73 @@
1
+ import Mux from "@mux/mux-node";
2
+ import { ObjectCannedACL, S3ClientConfig } from "@aws-sdk/client-s3";
3
+ import { KvStore, Upload } from "@tus/utils";
4
+ import fs from "node:fs";
5
+
6
+ //#region src/types/index.d.ts
7
+ interface MediaCloudPluginOptions {
8
+ enabled?: boolean;
9
+ mux: {
10
+ assetOptions: Partial<Mux.Video.Assets.AssetOptions>;
11
+ testMode?: boolean;
12
+ tokenId: string;
13
+ tokenSecret: string;
14
+ webhookSecret: string;
15
+ };
16
+ s3: {
17
+ accessKeyId: string;
18
+ acl?: string;
19
+ bucket: string;
20
+ endpoint?: string;
21
+ region: string;
22
+ secretAccessKey: string;
23
+ };
24
+ }
25
+ interface S3StoreConfig {
26
+ cache?: KvStore<TusUploadMetadata>;
27
+ expirationPeriodInMilliseconds?: number;
28
+ maxConcurrentPartUploads?: number;
29
+ /**
30
+ * The maximum number of parts allowed in a multipart upload. Defaults to 10,000.
31
+ */
32
+ maxMultipartParts?: number;
33
+ /**
34
+ * The minimal part size for parts.
35
+ * Can be used to ensure that all non-trailing parts are exactly the same size.
36
+ * Can not be lower than 5MiB or more than 5GiB.
37
+ */
38
+ minPartSize?: number;
39
+ /**
40
+ * The preferred part size for parts send to S3. Can not be lower than 5MiB or more than 5GiB.
41
+ * The server calculates the optimal part size, which takes this size into account,
42
+ * but may increase it to not exceed the S3 10K parts limit.
43
+ */
44
+ partSize?: number;
45
+ s3ClientConfig: {
46
+ acl?: ObjectCannedACL;
47
+ bucket: string;
48
+ } & S3ClientConfig;
49
+ useTags?: boolean;
50
+ }
51
+ interface TusUploadMetadata {
52
+ file: Upload;
53
+ 'tus-version': string;
54
+ 'upload-id': string;
55
+ }
56
+ interface IncompletePartInfo {
57
+ createReader: (options: {
58
+ cleanUpOnEnd: boolean;
59
+ }) => fs.ReadStream;
60
+ path: string;
61
+ size: number;
62
+ }
63
+ interface AWSError extends Error {
64
+ Code?: string;
65
+ code?: string;
66
+ }
67
+ interface NodeFSError extends Error {
68
+ code?: string;
69
+ }
70
+ type StaticRenditions = Record<string, unknown> | null | undefined;
71
+ //#endregion
72
+ export { AWSError, IncompletePartInfo, MediaCloudPluginOptions, NodeFSError, S3StoreConfig, StaticRenditions, TusUploadMetadata };
73
+ //# sourceMappingURL=index.d.ts.map
File without changes
@@ -0,0 +1,30 @@
1
+ //#region src/utils/file.d.ts
2
+ /**
3
+ * Checks if a file is a video file supported by Mux
4
+ * @param file - The file to check
5
+ * @returns Promise that resolves to true if the file is a supported video format, false otherwise
6
+ */
7
+ declare function isVideo(file: File): Promise<boolean>;
8
+ /**
9
+ * Gets the MIME type of a file using file-type library
10
+ * @param file - The file to check
11
+ * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined
12
+ */
13
+ declare function getFileType(file: File): Promise<string | undefined>;
14
+ /**
15
+ * Generates a unique filename by appending a random suffix if needed
16
+ * @param originalFilename - The original filename
17
+ * @returns A unique filename with a random suffix appended before the extension
18
+ */
19
+ declare function generateUniqueFilename(originalFilename: string): string;
20
+ /**
21
+ * Sanitizes a filename by removing/replacing invalid characters and handling edge cases
22
+ * Converts all non-alphanumeric characters (except dots for extensions) to underscores
23
+ * @param filename - The filename to sanitize
24
+ * @returns A sanitized filename safe for all operating systems
25
+ * @throws {TypeError} When filename is not a string
26
+ */
27
+ declare function sanitizeFilename(filename: string): string;
28
+ //#endregion
29
+ export { generateUniqueFilename, getFileType, isVideo, sanitizeFilename };
30
+ //# sourceMappingURL=file.d.ts.map
@@ -0,0 +1,84 @@
1
+ import { MediaCloudError } from "../types/errors.js";
2
+ import { useErrorHandler } from "../hooks/useErrorHandler.js";
3
+ import { fileTypeFromBuffer } from "file-type";
4
+ import { parse } from "pathe";
5
+
6
+ //#region src/utils/file.ts
7
+ const { logError } = useErrorHandler();
8
+ const MUX_SUPPORTED_VIDEO_FORMATS = new Set([
9
+ "application/mxf",
10
+ "video/3gpp",
11
+ "video/3gpp2",
12
+ "video/mp2t",
13
+ "video/mp4",
14
+ "video/mpeg",
15
+ "video/quicktime",
16
+ "video/webm",
17
+ "video/x-f4v",
18
+ "video/x-flv",
19
+ "video/x-matroska",
20
+ "video/x-ms-asf",
21
+ "video/x-ms-wmv",
22
+ "video/x-msvideo",
23
+ "video/x-mxf"
24
+ ]);
25
+ /**
26
+ * Checks if a file is a video file supported by Mux
27
+ * @param file - The file to check
28
+ * @returns Promise that resolves to true if the file is a supported video format, false otherwise
29
+ */
30
+ async function isVideo(file) {
31
+ try {
32
+ const buffer = new Uint8Array(await file.arrayBuffer());
33
+ const fileType = await fileTypeFromBuffer(buffer);
34
+ return fileType?.mime ? MUX_SUPPORTED_VIDEO_FORMATS.has(fileType.mime) : false;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+ /**
40
+ * Gets the MIME type of a file using file-type library
41
+ * @param file - The file to check
42
+ * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined
43
+ */
44
+ async function getFileType(file) {
45
+ try {
46
+ const buffer = new Uint8Array(await file.arrayBuffer());
47
+ const fileType = await fileTypeFromBuffer(buffer);
48
+ return fileType?.mime;
49
+ } catch (_error) {
50
+ logError(MediaCloudError.FILE_TYPE_ERROR);
51
+ return void 0;
52
+ }
53
+ }
54
+ /**
55
+ * Generates a unique filename by appending a random suffix if needed
56
+ * @param originalFilename - The original filename
57
+ * @returns A unique filename with a random suffix appended before the extension
58
+ */
59
+ function generateUniqueFilename(originalFilename) {
60
+ const timestamp = Date.now().toString(36);
61
+ const { name, ext } = parse(originalFilename);
62
+ return `${name}-${timestamp}${ext}`;
63
+ }
64
+ /**
65
+ * Sanitizes a filename by removing/replacing invalid characters and handling edge cases
66
+ * Converts all non-alphanumeric characters (except dots for extensions) to underscores
67
+ * @param filename - The filename to sanitize
68
+ * @returns A sanitized filename safe for all operating systems
69
+ * @throws {TypeError} When filename is not a string
70
+ */
71
+ function sanitizeFilename(filename) {
72
+ try {
73
+ const parsed = parse(filename);
74
+ const sanitized = parsed.name.replace(/[^a-zA-Z0-9_.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "");
75
+ return `${sanitized}${parsed.ext}`;
76
+ } catch (_error) {
77
+ logError(MediaCloudError.FILENAME_SANITIZE_ERROR);
78
+ return filename;
79
+ }
80
+ }
81
+
82
+ //#endregion
83
+ export { generateUniqueFilename, getFileType, isVideo, sanitizeFilename };
84
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","names":["file: File","originalFilename: string","filename: string"],"sources":["../../src/utils/file.ts"],"sourcesContent":["import { fileTypeFromBuffer } from 'file-type'\nimport { parse } from 'pathe'\n\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudError } from '../types/errors'\n\nconst { logError } = useErrorHandler()\n\nconst MUX_SUPPORTED_VIDEO_FORMATS = new Set([\n 'application/mxf',\n 'video/3gpp',\n 'video/3gpp2',\n 'video/mp2t',\n 'video/mp4',\n 'video/mpeg',\n 'video/quicktime',\n 'video/webm',\n 'video/x-f4v',\n 'video/x-flv',\n 'video/x-matroska',\n 'video/x-ms-asf',\n 'video/x-ms-wmv',\n 'video/x-msvideo',\n 'video/x-mxf',\n])\n\n/**\n * Checks if a file is a video file supported by Mux\n * @param file - The file to check\n * @returns Promise that resolves to true if the file is a supported video format, false otherwise\n */\nexport async function isVideo(file: File): Promise<boolean> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n\n return fileType?.mime\n ? MUX_SUPPORTED_VIDEO_FORMATS.has(fileType.mime)\n : false\n } catch {\n return false\n }\n}\n\n/**\n * Gets the MIME type of a file using file-type library\n * @param file - The file to check\n * @returns Promise that resolves to the MIME type of the file or undefined if it cannot be determined\n */\nexport async function getFileType(file: File): Promise<string | undefined> {\n try {\n const buffer = new Uint8Array(await file.arrayBuffer())\n const fileType = await fileTypeFromBuffer(buffer)\n return fileType?.mime\n } catch (_error) {\n logError(MediaCloudError.FILE_TYPE_ERROR)\n return undefined\n }\n}\n\n/**\n * Generates a unique filename by appending a random suffix if needed\n * @param originalFilename - The original filename\n * @returns A unique filename with a random suffix appended before the extension\n */\nexport function generateUniqueFilename(originalFilename: string): string {\n const timestamp = Date.now().toString(36)\n const { name, ext } = parse(originalFilename)\n return `${name}-${timestamp}${ext}`\n}\n\n/**\n * Sanitizes a filename by removing/replacing invalid characters and handling edge cases\n * Converts all non-alphanumeric characters (except dots for extensions) to underscores\n * @param filename - The filename to sanitize\n * @returns A sanitized filename safe for all operating systems\n * @throws {TypeError} When filename is not a string\n */\nexport function sanitizeFilename(filename: string): string {\n try {\n const parsed = parse(filename)\n const sanitized = parsed.name\n .replace(/[^a-zA-Z0-9_.-]/g, '_')\n .replace(/_{2,}/g, '_')\n .replace(/^_|_$/g, '')\n return `${sanitized}${parsed.ext}`\n } catch (_error) {\n logError(MediaCloudError.FILENAME_SANITIZE_ERROR)\n return filename\n }\n}\n"],"mappings":";;;;;;AAMA,MAAM,EAAE,UAAU,GAAG,iBAAiB;AAEtC,MAAM,8BAA8B,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;;AAOD,eAAsB,QAAQA,MAA8B;AAC1D,KAAI;EACF,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,aAAa;EACtD,MAAM,WAAW,MAAM,mBAAmB,OAAO;AAEjD,SAAO,UAAU,OACb,4BAA4B,IAAI,SAAS,KAAK,GAC9C;CACL,QAAO;AACN,SAAO;CACR;AACF;;;;;;AAOD,eAAsB,YAAYA,MAAyC;AACzE,KAAI;EACF,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,aAAa;EACtD,MAAM,WAAW,MAAM,mBAAmB,OAAO;AACjD,SAAO,UAAU;CAClB,SAAQ,QAAQ;EACf,SAAS,gBAAgB,gBAAgB;AACzC,SAAO;CACR;AACF;;;;;;AAOD,SAAgB,uBAAuBC,kBAAkC;CACvE,MAAM,YAAY,KAAK,KAAK,CAAC,SAAS,GAAG;CACzC,MAAM,EAAE,MAAM,KAAK,GAAG,MAAM,iBAAiB;AAC7C,QAAO,GAAG,KAAK,CAAC,EAAE,YAAY,KAAK;AACpC;;;;;;;;AASD,SAAgB,iBAAiBC,UAA0B;AACzD,KAAI;EACF,MAAM,SAAS,MAAM,SAAS;EAC9B,MAAM,YAAY,OAAO,KACtB,QAAQ,oBAAoB,IAAI,CAChC,QAAQ,UAAU,IAAI,CACtB,QAAQ,UAAU,GAAG;AACxB,SAAO,GAAG,YAAY,OAAO,KAAK;CACnC,SAAQ,QAAQ;EACf,SAAS,gBAAgB,wBAAwB;AACjD,SAAO;CACR;AACF"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@maas/payload-plugin-media-cloud",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "contributors": [
6
+ {
7
+ "name": "Robin Scholz",
8
+ "url": "https://github.com/robinscholz"
9
+ },
10
+ {
11
+ "name": "Christoph Jeworutzki",
12
+ "url": "https://github.com/ChristophJeworutzki"
13
+ }
14
+ ],
15
+ "keywords": [],
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ },
26
+ "./components": {
27
+ "types": "./dist/components/index.d.ts",
28
+ "import": "./dist/components/index.js"
29
+ }
30
+ },
31
+ "sideEffects": false,
32
+ "scripts": {
33
+ "build": "tsdown",
34
+ "dev": "tsdown --watch",
35
+ "lint": "eslint .",
36
+ "format": "prettier --write .",
37
+ "prepack": "pnpm resolve-catalog",
38
+ "postpack": "pnpm restore-catalog",
39
+ "resolve-catalog": "pnpx tsx ../../scripts/resolve-catalog.ts",
40
+ "restore-catalog": "pnpx tsx ../../scripts/restore-catalog.ts",
41
+ "release": "release-it"
42
+ },
43
+ "dependencies": {
44
+ "@aws-sdk/client-s3": "^3.859.0",
45
+ "@maas/error-handler": "workspace:*",
46
+ "@mux/mux-node": "^12.3.0",
47
+ "@mux/upchunk": "^3.5.0",
48
+ "@tus/server": "^2.3.0",
49
+ "@tus/utils": "^0.6.0",
50
+ "file-type": "^21.0.0",
51
+ "mitt": "^3.0.1",
52
+ "multistream": "^4.1.0",
53
+ "pathe": "^2.0.3",
54
+ "tus-js-client": "^4.3.1"
55
+ },
56
+ "peerDependencies": {
57
+ "@mux/mux-player-react": "^3",
58
+ "@payloadcms/plugin-cloud-storage": "^3.49",
59
+ "@payloadcms/ui": "^3.49",
60
+ "payload": "^3.49",
61
+ "react": "^19.1",
62
+ "react-dom": "^19.1"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "@mux/mux-player-react": {
66
+ "optional": false
67
+ },
68
+ "@payloadcms/plugin-cloud-storage": {
69
+ "optional": false
70
+ },
71
+ "@payloadcms/ui": {
72
+ "optional": false
73
+ },
74
+ "payload": {
75
+ "optional": false
76
+ },
77
+ "react": {
78
+ "optional": false
79
+ },
80
+ "react-dom": {
81
+ "optional": false
82
+ }
83
+ },
84
+ "devDependencies": {
85
+ "@types/multistream": "^4.1.3",
86
+ "@types/node": "^22.17.0",
87
+ "@types/react": "^19.1.7",
88
+ "glob": "^11.0.3",
89
+ "tsdown": "^0.13.1",
90
+ "typescript": "5.8.3"
91
+ }
92
+ }