From d70d4121158547b3217664da498d2bcdae0a73c0 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 26 Sep 2025 08:00:29 -0700 Subject: [PATCH] feat: DAV Locks support + refactor of DAV method handler (#1486) * feat: DAV Locks support + refactor of DAV method handler * format: Restore old whitespace for CoreModule.js * fix: options route registering after likecycle hooks --------- Co-authored-by: Neal Shah <30693865+ProgrammerIn-wonderland@users.noreply.github.com> --- eslint.config.js | 125 +- package-lock.json | 2579 +++++++---------- package.json | 4 +- src/backend/src/CoreModule.js | 2 +- .../kvstore/KVStoreInterfaceService.js | 22 +- .../src/modules/web/WebServerService.js | 26 +- .../src/services/WebDAV/WebDAVService.js | 312 ++ src/backend/src/services/WebDAV/lockStore.mjs | 160 + .../services/WebDAV/methodHandlers/COPY.mjs | 115 + .../services/WebDAV/methodHandlers/DELETE.mjs | 43 + .../WebDAV/methodHandlers/HEAD_GET.mjs | 133 + .../services/WebDAV/methodHandlers/LOCK.mjs | 103 + .../services/WebDAV/methodHandlers/MKCOL.mjs | 90 + .../services/WebDAV/methodHandlers/MOVE.mjs | 118 + .../WebDAV/methodHandlers/OPTIONS.mjs | 14 + .../WebDAV/methodHandlers/PROPFIND.mjs | 177 ++ .../WebDAV/methodHandlers/PROPPATCH.mjs | 52 + .../services/WebDAV/methodHandlers/PUT.mjs | 109 + .../services/WebDAV/methodHandlers/UNLOCK.mjs | 39 + .../services/WebDAV/methodHandlers/method.mjs | 27 + .../WebDAV/methodHandlers/methodMap.mjs | 30 + src/backend/src/services/WebDAV/utils.mjs | 170 ++ src/backend/src/services/WebDavFS.js | 1281 -------- src/puter-js/src/modules/KV.js | 75 +- 24 files changed, 2937 insertions(+), 2869 deletions(-) create mode 100644 src/backend/src/services/WebDAV/WebDAVService.js create mode 100644 src/backend/src/services/WebDAV/lockStore.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/COPY.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/DELETE.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/HEAD_GET.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/LOCK.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/MKCOL.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/MOVE.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/OPTIONS.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/PROPFIND.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/PROPPATCH.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/PUT.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/UNLOCK.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/method.mjs create mode 100644 src/backend/src/services/WebDAV/methodHandlers/methodMap.mjs create mode 100644 src/backend/src/services/WebDAV/utils.mjs delete mode 100644 src/backend/src/services/WebDavFS.js diff --git a/eslint.config.js b/eslint.config.js index e7da01b95..6faf68546 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -4,6 +4,48 @@ import { defineConfig } from 'eslint/config'; import globals from 'globals'; import controlStructureSpacing from './control-structure-spacing.js'; +const rules = { + 'no-unused-vars': ['error', { + 'vars': 'all', + 'args': 'after-used', + 'caughtErrors': 'all', + 'ignoreRestSiblings': false, + 'ignoreUsingDeclarations': false, + 'reportUsedIgnorePattern': false, + 'argsIgnorePattern': '^_', + 'caughtErrorsIgnorePattern': '^_', + 'destructuredArrayIgnorePattern': '^_', + }], + '@stylistic/curly-newline': ['error', 'always'], + '@stylistic/object-curly-spacing': ['error', 'always'], + '@stylistic/indent': ['error', 4, { + 'CallExpression': { arguments: 4 }, + }], + '@stylistic/indent-binary-ops': ['error', 4], + '@stylistic/array-bracket-newline': ['error', 'consistent'], + '@stylistic/semi': ['error', 'always'], + '@stylistic/quotes': ['error', 'single'], + '@stylistic/function-call-argument-newline': ['error', 'consistent'], + '@stylistic/arrow-spacing': ['error', { before: true, after: true }], + '@stylistic/space-before-function-paren': ['error', { 'anonymous': 'never', 'named': 'never', 'asyncArrow': 'always', 'catch': 'never' }], + '@stylistic/key-spacing': ['error', { 'beforeColon': false, 'afterColon': true }], + '@stylistic/keyword-spacing': ['error', { 'before': true, 'after': true }], + '@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }], + '@stylistic/comma-spacing': ['error', { 'before': false, 'after': true }], + '@stylistic/comma-dangle': ['error', 'always-multiline'], + '@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }], + '@stylistic/dot-location': ['error', 'property'], + '@stylistic/space-infix-ops': ['error'], + 'no-template-curly-in-string': 'error', + 'prefer-template': 'error', + 'no-undef': 'error', + 'no-useless-concat': 'error', + 'template-curly-spacing': ['error', 'never'], + curly: ['error', 'multi-line'], + 'custom/control-structure-spacing': 'error', + '@stylistic/no-trailing-spaces': 'error', +}; + export default defineConfig([ { plugins: { @@ -13,49 +55,9 @@ export default defineConfig([ }, }, { - files: ['src/backend/**/*.{js,mjs,cjs}'], + files: ['**/backend/**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.node }, - rules: { - 'no-unused-vars': ['error', { - 'vars': 'all', - 'args': 'after-used', - 'caughtErrors': 'all', - 'ignoreRestSiblings': false, - 'ignoreUsingDeclarations': false, - 'reportUsedIgnorePattern': false, - 'argsIgnorePattern': '^_', - 'caughtErrorsIgnorePattern': '^_', - 'destructuredArrayIgnorePattern': '^_', - - }], - curly: ['error', 'multi-line'], - '@stylistic/curly-newline': ['error', 'always'], - '@stylistic/object-curly-spacing': ['error', 'always'], - '@stylistic/indent': ['error', 4, { - CallExpression: { - arguments: 4, - }, - }], - '@stylistic/indent-binary-ops': ['error', 4], - '@stylistic/array-bracket-newline': ['error', 'consistent'], - '@stylistic/semi': ['error', 'always'], - '@stylistic/quotes': 'off', - '@stylistic/function-call-argument-newline': ['error', 'consistent'], - '@stylistic/arrow-spacing': ['error', { before: true, after: true }], - '@stylistic/space-before-function-paren': ['error', { 'anonymous': 'never', 'named': 'never', 'asyncArrow': 'always', 'catch': 'never' }], - '@stylistic/key-spacing': ['error', { 'beforeColon': false, 'afterColon': true }], - '@stylistic/keyword-spacing': ['error', { 'before': true, 'after': true }], - '@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }], - '@stylistic/comma-spacing': ['error', { 'before': false, 'after': true }], - '@stylistic/comma-dangle': ['error', 'always-multiline'], - '@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }], - '@stylistic/dot-location': ['error', 'property'], - '@stylistic/space-infix-ops': ['error'], - 'no-undef': 'error', - 'custom/control-structure-spacing': 'error', - '@stylistic/no-trailing-spaces': 'error', - - }, + rules, extends: ['js/recommended'], plugins: { js, @@ -66,44 +68,7 @@ export default defineConfig([ files: ['**/*.{js,mjs,cjs}'], ignores: ['src/backend/**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.browser }, - rules: { - - 'no-unused-vars': ['error', { - 'vars': 'all', - 'args': 'after-used', - 'caughtErrors': 'all', - 'ignoreRestSiblings': false, - 'ignoreUsingDeclarations': false, - 'reportUsedIgnorePattern': false, - 'argsIgnorePattern': '^_', - 'caughtErrorsIgnorePattern': '^_', - 'destructuredArrayIgnorePattern': '^_', - }], - '@stylistic/curly-newline': ['error', 'always'], - '@stylistic/object-curly-spacing': ['error', 'always'], - '@stylistic/indent': ['error', 4, { - 'CallExpression': { arguments: 4 }, - }], - '@stylistic/indent-binary-ops': ['error', 4], - '@stylistic/array-bracket-newline': ['error', 'consistent'], - '@stylistic/semi': ['error', 'always'], - '@stylistic/quotes': ['error', 'single'], - '@stylistic/function-call-argument-newline': ['error', 'consistent'], - '@stylistic/arrow-spacing': ['error', { before: true, after: true }], - '@stylistic/space-before-function-paren': ['error', { 'anonymous': 'never', 'named': 'never', 'asyncArrow': 'always', 'catch': 'never' }], - '@stylistic/key-spacing': ['error', { 'beforeColon': false, 'afterColon': true }], - '@stylistic/keyword-spacing': ['error', { 'before': true, 'after': true }], - '@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }], - '@stylistic/comma-spacing': ['error', { 'before': false, 'after': true }], - '@stylistic/comma-dangle': ['error', 'always-multiline'], - '@stylistic/object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }], - '@stylistic/dot-location': ['error', 'property'], - '@stylistic/space-infix-ops': ['error'], - 'no-undef': 'error', - curly: ['error', 'multi-line'], - 'custom/control-structure-spacing': 'error', - '@stylistic/no-trailing-spaces': 'error', - }, + rules, extends: ['js/recommended'], plugins: { js, diff --git a/package-lock.json b/package-lock.json index 79b786508..4c463c4d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,9 @@ "@google/genai": "^1.19.0", "@heyputer/putility": "^1.0.2", "@paralleldrive/cuid2": "^2.2.2", + "@stylistic/eslint-plugin-js": "^4.4.1", "dedent": "^1.5.3", + "express-xml-bodyparser": "^0.4.1", "ioredis": "^5.6.0", "javascript-time-ago": "^2.5.11", "json-colorizer": "^3.0.1", @@ -36,7 +38,7 @@ "dotenv": "^16.4.5", "eslint": "^9.35.0", "express": "^4.18.2", - "globals": "^15.0.0", + "globals": "^15.15.0", "html-entities": "^2.3.3", "html-webpack-plugin": "^5.6.0", "husky": "^9.1.7", @@ -191,50 +193,50 @@ } }, "node_modules/@aws-sdk/client-polly": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-polly/-/client-polly-3.883.0.tgz", - "integrity": "sha512-E/2r9tj+PmY4XktjBPVnT+qih4Vw9Hw359WMBYvLmTHJUoKGyaYvlj/XMCBMXzjwC3QcBLckWUjjaHpuPCD0fg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-polly/-/client-polly-3.887.0.tgz", + "integrity": "sha512-BBRUiFUjxhWKt7yeWgZS0jw5loe8WT8eZtclJu+OTsd1kphUtpjYt1ZBkNCfVhu5FsldS0pjGphLRqq0dLGElg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.883.0", - "@aws-sdk/credential-provider-node": "3.883.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.883.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.883.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.2", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.21", - "@smithy/middleware-retry": "^4.1.22", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.29", - "@smithy/util-defaults-mode-node": "^4.0.29", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/credential-provider-node": "3.887.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.887.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.887.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-stream": "^4.3.1", + "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, "engines": { @@ -242,49 +244,49 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.883.0.tgz", - "integrity": "sha512-qpt9oRPES5+DkE2vBmwu9FPcLIHQPj35hmLkupNptiXEYqN5eUJV/nINDUHJgyEsPN05Dc+847R+QH5u1rSFVg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.887.0.tgz", + "integrity": "sha512-yGxML5pGTWZ3yY0ocYkD5F1y3kiLQ80zLuBfAjhCoAW3hnP9/ng8vGxga/vOpVKIX30DXNzHrcEnQB4oRcn8cA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.883.0", - "@aws-sdk/credential-provider-node": "3.883.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.883.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.883.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.2", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.21", - "@smithy/middleware-retry": "^4.1.22", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.29", - "@smithy/util-defaults-mode-node": "^4.0.29", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/credential-provider-node": "3.887.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.887.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.887.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" @@ -294,48 +296,48 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.883.0.tgz", - "integrity": "sha512-Ybjw76yPceEBO7+VLjy5+/Gr0A1UNymSDHda5w8tfsS2iHZt/vuD6wrYpHdLoUx4H5la8ZhwcSfK/+kmE+QLPw==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.887.0.tgz", + "integrity": "sha512-ZKN8BxkRdC6vK6wlnuLSYBhj7uufg14GP5bxqiRaDEooN1y2WcuY95GP13I3brLvM0uboFGbObIVpVrbeHifng==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.883.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.883.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.883.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.2", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.21", - "@smithy/middleware-retry": "^4.1.22", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.29", - "@smithy/util-defaults-mode-node": "^4.0.29", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.887.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.887.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, "engines": { @@ -343,49 +345,49 @@ } }, "node_modules/@aws-sdk/client-textract": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-textract/-/client-textract-3.883.0.tgz", - "integrity": "sha512-UC+xE0GKykbk6NOIldxo8xld+XTJA1RMthssmv5FHE1g0rRpwOuEKxKmyOoKNn20DGIatWxZ5AMJJJhIW845Jw==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-textract/-/client-textract-3.887.0.tgz", + "integrity": "sha512-HV97c4ZY8QI/tAnr2ezUOU+esOlf/RfjW29Ozj3lW9pXatqcAn72dSjqfKrJPwmz3Xm7Ic0TMb6tU8eZ3HVRWw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.883.0", - "@aws-sdk/credential-provider-node": "3.883.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.883.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.883.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.2", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.21", - "@smithy/middleware-retry": "^4.1.22", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.29", - "@smithy/util-defaults-mode-node": "^4.0.29", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/credential-provider-node": "3.887.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.887.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.887.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" @@ -395,24 +397,24 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.883.0.tgz", - "integrity": "sha512-FmkqnqBLkXi4YsBPbF6vzPa0m4XKUuvgKDbamfw4DZX2CzfBZH6UU4IwmjNV3ZM38m0xraHarK8KIbGSadN3wg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.887.0.tgz", + "integrity": "sha512-oiBsWhuuj1Lzh+FHY+gE0PyYuiDxqFf98F9Pd2WruY5Gu/+/xvDFEPEkIEOae8gWRaLZ5Eh8u+OY9LS4DXZhuQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/xml-builder": "3.873.0", - "@smithy/core": "^3.9.2", - "@smithy/node-config-provider": "^4.1.4", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/xml-builder": "3.887.0", + "@smithy/core": "^3.11.0", + "@smithy/node-config-provider": "^4.2.1", "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", + "@smithy/protocol-http": "^5.2.1", "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, @@ -421,15 +423,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.883.0.tgz", - "integrity": "sha512-Z6tPBXPCodfhIF1rvQKoeRGMkwL6TK0xdl1UoMIA1x4AfBpPICAF77JkFBExk/pdiFYq1d04Qzddd/IiujSlLg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.887.0.tgz", + "integrity": "sha512-kv7L5E8mxlWTMhCK639wrQnFEmwUDfKvKzTMDo2OboXZ0iSbD+hBPoT0gkb49qHNetYnsl63BVOxc0VNiOA04w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -437,20 +439,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.883.0.tgz", - "integrity": "sha512-P589ug1lMOOEYLTaQJjSP+Gee34za8Kk2LfteNQfO9SpByHFgGj++Sg8VyIe30eZL8Q+i4qTt24WDCz1c+dgYg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.887.0.tgz", + "integrity": "sha512-siLttHxSFgJ5caDgS+BHYs9GBDX7J3pgge4OmJvIQeGO+KaJC12TerBNPJOp+qRaRC3yuVw3T9RpSZa8mmaiyA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/types": "3.862.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/node-http-handler": "^4.2.1", "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/util-stream": "^4.3.1", "tslib": "^2.6.2" }, "engines": { @@ -458,23 +460,23 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.883.0.tgz", - "integrity": "sha512-n6z9HTzuDEdugXvPiE/95VJXbF4/gBffdV/SRHDJKtDHaRuvp/gggbfmfVSTFouGVnlKPb2pQWQsW3Nr/Y3Lrw==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.887.0.tgz", + "integrity": "sha512-Na9IjKdPuSNU/mBcCQ49HiIgomq/O7kZAuRyGwAXiRPbf86AacKv4dsUyPZY6lCgVIvVniRWgYlVaPgq22EIig==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/credential-provider-env": "3.883.0", - "@aws-sdk/credential-provider-http": "3.883.0", - "@aws-sdk/credential-provider-process": "3.883.0", - "@aws-sdk/credential-provider-sso": "3.883.0", - "@aws-sdk/credential-provider-web-identity": "3.883.0", - "@aws-sdk/nested-clients": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/credential-provider-env": "3.887.0", + "@aws-sdk/credential-provider-http": "3.887.0", + "@aws-sdk/credential-provider-process": "3.887.0", + "@aws-sdk/credential-provider-sso": "3.887.0", + "@aws-sdk/credential-provider-web-identity": "3.887.0", + "@aws-sdk/nested-clients": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -482,22 +484,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.883.0.tgz", - "integrity": "sha512-QIUhsatsrwfB9ZsKpmi0EySSfexVP61wgN7hr493DOileh2QsKW4XATEfsWNmx0dj9323Vg1Mix7bXtRfl9cGg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.887.0.tgz", + "integrity": "sha512-iJdCq/brBWYpJzJcXY2UhEoW7aA28ixIpvLKjxh5QUBfjCj19cImpj1gGwTIs6/fVcjVUw1tNveTBfn1ziTzVg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.883.0", - "@aws-sdk/credential-provider-http": "3.883.0", - "@aws-sdk/credential-provider-ini": "3.883.0", - "@aws-sdk/credential-provider-process": "3.883.0", - "@aws-sdk/credential-provider-sso": "3.883.0", - "@aws-sdk/credential-provider-web-identity": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/credential-provider-env": "3.887.0", + "@aws-sdk/credential-provider-http": "3.887.0", + "@aws-sdk/credential-provider-ini": "3.887.0", + "@aws-sdk/credential-provider-process": "3.887.0", + "@aws-sdk/credential-provider-sso": "3.887.0", + "@aws-sdk/credential-provider-web-identity": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/credential-provider-imds": "^4.0.7", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -505,16 +507,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.883.0.tgz", - "integrity": "sha512-m1shbHY/Vppy4EdddG9r8x64TO/9FsCjokp5HbKcZvVoTOTgUJrdT8q2TAQJ89+zYIJDqsKbqfrmfwJ1zOdnGQ==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.887.0.tgz", + "integrity": "sha512-J5TIrQ/DUiyR65gXt1j3TEbLUwMcgYVB/G68/AVgBptPvb9kj+6zFG67bJJHwxtqJxRLVLTtTi9u/YDXTqGBpQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -522,18 +524,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.883.0.tgz", - "integrity": "sha512-37ve9Tult08HLXrJFHJM/sGB/vO7wzI6v1RUUfeTiShqx8ZQ5fTzCTNY/duO96jCtCexmFNSycpQzh7lDIf0aA==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.887.0.tgz", + "integrity": "sha512-Bv9wUActLu6Kn0MK2s72bgbbNxSLPVop/If4MVbCyJ3n+prJnm5RsTF3isoWQVyyXA5g4tIrS8mE5FpejSbyPQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.883.0", - "@aws-sdk/core": "3.883.0", - "@aws-sdk/token-providers": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/client-sso": "3.887.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/token-providers": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -541,16 +543,16 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.883.0.tgz", - "integrity": "sha512-SL82K9Jb0vpuTadqTO4Fpdu7SKtebZ3Yo4LZvk/U0UauVMlJj5ZTos0mFx1QSMB9/4TpqifYrSZcdnxgYg8Eqw==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.887.0.tgz", + "integrity": "sha512-PRh0KRukY2euN9xvvQ3cqhCAlEkMDJIWDLIfxQ1hTbv7JA3hrcLVrV+Jg5FRWsStDhweHIvD/VzruSkhJQS80g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/nested-clients": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/nested-clients": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -558,14 +560,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz", - "integrity": "sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.887.0.tgz", + "integrity": "sha512-ulzqXv6NNqdu/kr0sgBYupWmahISHY+azpJidtK6ZwQIC+vBUk9NdZeqQpy7KVhIk2xd4+5Oq9rxapPwPI21CA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", + "@aws-sdk/types": "3.887.0", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -573,13 +575,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.876.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz", - "integrity": "sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.887.0.tgz", + "integrity": "sha512-YbbgLI6jKp2qSoAcHnXrQ5jcuc5EYAmGLVFgMVdk8dfCfJLfGGSaOLxF4CXC7QYhO50s+mPPkhBYejCik02Kug==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", + "@aws-sdk/types": "3.887.0", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -587,14 +589,15 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.873.0.tgz", - "integrity": "sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.887.0.tgz", + "integrity": "sha512-tjrUXFtQnFLo+qwMveq5faxP5MQakoLArXtqieHphSqZTXm21wDJM73hgT4/PQQGTwgYjDKqnqsE1hvk0hcfDw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", + "@aws-sdk/types": "3.887.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -602,17 +605,17 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.883.0.tgz", - "integrity": "sha512-q58uLYnGLg7hsnWpdj7Cd1Ulsq1/PUJOHvAfgcBuiDE/+Fwh0DZxZZyjrU+Cr+dbeowIdUaOO8BEDDJ0CUenJw==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.887.0.tgz", + "integrity": "sha512-YjBz2J4l3uCeMv2g1natat5YSMRZYdEpEg60g3d7q6hoHUD10SmWy8M+Ca8djF0is70vPmF3Icm2cArK3mtoNA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@smithy/core": "^3.9.2", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@smithy/core": "^3.11.0", + "@smithy/protocol-http": "^5.2.1", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -620,48 +623,48 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.883.0.tgz", - "integrity": "sha512-IhzDM+v0ga53GOOrZ9jmGNr7JU5OR6h6ZK9NgB7GXaa+gsDbqfUuXRwyKDYXldrTXf1sUR3vy1okWDXA7S2ejQ==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.887.0.tgz", + "integrity": "sha512-h6/dHuAJhJnhSDihcQd0wfJBZoPmPajASVqGk8qDxYDBWxIU9/mYcKvM+kTrKw3f9Wf3S/eR5B/rYHHuxFheSw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.883.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.883.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.883.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.2", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.21", - "@smithy/middleware-retry": "^4.1.22", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.2", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.29", - "@smithy/util-defaults-mode-node": "^4.0.29", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/middleware-host-header": "3.887.0", + "@aws-sdk/middleware-logger": "3.887.0", + "@aws-sdk/middleware-recursion-detection": "3.887.0", + "@aws-sdk/middleware-user-agent": "3.887.0", + "@aws-sdk/region-config-resolver": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@aws-sdk/util-endpoints": "3.887.0", + "@aws-sdk/util-user-agent-browser": "3.887.0", + "@aws-sdk/util-user-agent-node": "3.887.0", + "@smithy/config-resolver": "^4.2.1", + "@smithy/core": "^3.11.0", + "@smithy/fetch-http-handler": "^5.2.1", + "@smithy/hash-node": "^4.1.1", + "@smithy/invalid-dependency": "^4.1.1", + "@smithy/middleware-content-length": "^4.1.1", + "@smithy/middleware-endpoint": "^4.2.1", + "@smithy/middleware-retry": "^4.2.1", + "@smithy/middleware-serde": "^4.1.1", + "@smithy/middleware-stack": "^4.1.1", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/node-http-handler": "^4.2.1", + "@smithy/protocol-http": "^5.2.1", + "@smithy/smithy-client": "^4.6.1", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-base64": "^4.1.0", + "@smithy/util-body-length-browser": "^4.1.0", + "@smithy/util-body-length-node": "^4.1.0", + "@smithy/util-defaults-mode-browser": "^4.1.1", + "@smithy/util-defaults-mode-node": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", + "@smithy/util-middleware": "^4.1.1", + "@smithy/util-retry": "^4.1.1", + "@smithy/util-utf8": "^4.1.0", "tslib": "^2.6.2" }, "engines": { @@ -669,16 +672,16 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz", - "integrity": "sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.887.0.tgz", + "integrity": "sha512-VdSMrIqJ3yjJb/fY+YAxrH/lCVv0iL8uA+lbMNfQGtO5tB3Zx6SU9LEpUwBNX8fPK1tUpI65CNE4w42+MY/7Mg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", + "@aws-sdk/types": "3.887.0", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/types": "^4.5.0", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", + "@smithy/util-middleware": "^4.1.1", "tslib": "^2.6.2" }, "engines": { @@ -686,17 +689,17 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.883.0.tgz", - "integrity": "sha512-tcj/Z5paGn9esxhmmkEW7gt39uNoIRbXG1UwJrfKu4zcTr89h86PDiIE2nxUO3CMQf1KgncPpr5WouPGzkh/QQ==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.887.0.tgz", + "integrity": "sha512-3e5fTPMPeJ5DphZ+OSqzw4ymCgDf8SQVBgrlKVo4Bch9ZwmmAoOHbuQrXVa9xQHklEHJg1Gz2pkjxNaIgx7quA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.883.0", - "@aws-sdk/nested-clients": "3.883.0", - "@aws-sdk/types": "3.862.0", + "@aws-sdk/core": "3.887.0", + "@aws-sdk/nested-clients": "3.887.0", + "@aws-sdk/types": "3.887.0", "@smithy/property-provider": "^4.0.5", "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -704,12 +707,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", - "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.887.0.tgz", + "integrity": "sha512-fmTEJpUhsPsovQ12vZSpVTEP/IaRoJAMBGQXlQNjtCpkBp6Iq3KQDa/HDaPINE+3xxo6XvTdtibsNOd5zJLV9A==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -717,15 +720,15 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz", - "integrity": "sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.887.0.tgz", + "integrity": "sha512-kpegvT53KT33BMeIcGLPA65CQVxLUL/C3gTz9AzlU/SDmeusBHX4nRApAicNzI/ltQ5lxZXbQn18UczzBuwF1w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-endpoints": "^3.0.7", + "@aws-sdk/types": "3.887.0", + "@smithy/types": "^4.5.0", + "@smithy/url-parser": "^4.1.1", + "@smithy/util-endpoints": "^3.1.1", "tslib": "^2.6.2" }, "engines": { @@ -745,27 +748,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz", - "integrity": "sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.887.0.tgz", + "integrity": "sha512-X71UmVsYc6ZTH4KU6hA5urOzYowSXc3qvroagJNLJYU1ilgZ529lP4J9XOYfEvTXkLR1hPFSRxa43SrwgelMjA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", + "@aws-sdk/types": "3.887.0", + "@smithy/types": "^4.5.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.883.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.883.0.tgz", - "integrity": "sha512-28cQZqC+wsKUHGpTBr+afoIdjS6IoEJkMqcZsmo2Ag8LzmTa6BUWQenFYB0/9BmDy4PZFPUn+uX+rJgWKB+jzA==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.887.0.tgz", + "integrity": "sha512-eqnx2FWAf40Nw6EyhXWjVT5WYYMz0rLrKEhZR3GdRQyOFzgnnEfq74TtG2Xji9k/ODqkcKqkiI52RYDEcdh8Jg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.883.0", - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", + "@aws-sdk/middleware-user-agent": "3.887.0", + "@aws-sdk/types": "3.887.0", + "@smithy/node-config-provider": "^4.2.1", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { @@ -781,18 +784,27 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz", - "integrity": "sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==", + "version": "3.887.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.887.0.tgz", + "integrity": "sha512-lMwgWK1kNgUhHGfBvO/5uLe7TKhycwOn3eRCqsKPT9aPCx/HWuTlpcQp8oW2pCRGLS7qzcxqpQulcD+bbUL7XQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.3.2", + "@smithy/types": "^4.5.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", + "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -855,31 +867,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1090,29 +1077,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/@babel/types": { "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", @@ -1618,7 +1582,6 @@ "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" @@ -1637,7 +1600,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1650,7 +1612,6 @@ "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1660,7 +1621,6 @@ "version": "0.21.0", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", @@ -1671,60 +1631,10 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-array/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/@eslint/config-helpers": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1734,7 +1644,6 @@ "version": "0.15.2", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -1747,7 +1656,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", @@ -1767,40 +1675,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1809,31 +1687,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/@eslint/js": { "version": "9.35.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", - "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1846,7 +1703,6 @@ "version": "2.1.6", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1856,7 +1712,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.15.2", @@ -2175,6 +2030,73 @@ "node": ">=6" } }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@grpc/proto-loader/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/@hapi/b64": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-5.0.0.tgz", @@ -2290,6 +2212,30 @@ "minimatch": "^9.0.0" } }, + "node_modules/@heyputer/kv.js/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@heyputer/kv.js/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@heyputer/multest": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/@heyputer/multest/-/multest-0.0.2.tgz", @@ -2333,7 +2279,6 @@ "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" @@ -2343,7 +2288,6 @@ "version": "0.16.7", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", @@ -2357,7 +2301,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" @@ -2371,7 +2314,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -3506,9 +3448,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -3534,29 +3476,6 @@ "debug": "^4.1.1" } }, - "node_modules/@kwsites/file-exists/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@kwsites/file-exists/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", @@ -6788,6 +6707,22 @@ "eslint": ">=9.0.0" } }, + "node_modules/@stylistic/eslint-plugin-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.4.1.tgz", + "integrity": "sha512-eLisyHvx7Sel8vcFZOEwDEBGmYsYM1SqDn81BWgmbqEXfXRf8oe6Rwp+ryM/8odNjlxtaaxp0Ihmt86CnLAxKg==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -7764,15 +7699,6 @@ "node": ">= 0.6" } }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -7822,7 +7748,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -7853,29 +7778,6 @@ "node": ">= 6.0.0" } }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/agentkeepalive": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", @@ -7906,7 +7808,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -8084,7 +7985,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/args": { @@ -8479,6 +8379,21 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -8492,12 +8407,13 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -8722,7 +8638,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8822,7 +8737,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -8951,17 +8865,70 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, "engines": { - "node": ">=12" + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/clone": { @@ -9202,6 +9169,30 @@ "node": ">= 0.8.0" } }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9251,6 +9242,21 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/concurrently/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/concurrently/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9267,6 +9273,63 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/concurrently/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/concurrently/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -9332,9 +9395,9 @@ } }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -9353,6 +9416,15 @@ "node": ">= 0.8.0" } }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -9486,7 +9558,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -9590,12 +9661,20 @@ } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -9696,7 +9775,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, "license": "MIT" }, "node_modules/deepmerge": { @@ -10060,9 +10138,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.215", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.215.tgz", - "integrity": "sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ==", + "version": "1.5.217", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.217.tgz", + "integrity": "sha512-Pludfu5iBxp9XzNl0qq2G87hdD17ZV7h5T4n6rQXDi3nCyloBV3jreE9+8GC6g4X/5yxqVgXEURpcLtM0WS4jA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -10149,12 +10227,6 @@ } } }, - "node_modules/engine.io-client/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -10164,6 +10236,15 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/engine.io/node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -10181,12 +10262,6 @@ } } }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -10355,7 +10430,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -10390,7 +10464,6 @@ "version": "9.35.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", - "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", @@ -10451,7 +10524,6 @@ "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -10468,7 +10540,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10477,55 +10548,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -10539,7 +10561,6 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", @@ -10571,7 +10592,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -10724,15 +10744,33 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "node_modules/express-xml-bodyparser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/express-xml-bodyparser/-/express-xml-bodyparser-0.4.1.tgz", + "integrity": "sha512-PlojEEQXdwc68ofPiAanknPf4QBTrFWXPZ+5jDhfrXP/CdLaqEQxQuuzrCqnvy1kETciTxz6OFnDZW/rIxtmlQ==", "license": "MIT", + "dependencies": { + "xml2js": "^0.6.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=18.0" } }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -10786,14 +10824,12 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -10889,7 +10925,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" @@ -10977,6 +11012,21 @@ "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -10999,7 +11049,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -11080,7 +11129,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", @@ -11094,7 +11142,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, "license": "ISC" }, "node_modules/fn.name": { @@ -11381,23 +11428,6 @@ "node": ">= 14" } }, - "node_modules/gaxios/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/gaxios/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -11411,12 +11441,6 @@ "node": ">= 14" } }, - "node_modules/gaxios/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/gcp-metadata": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", @@ -11580,28 +11604,6 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "license": "BSD-2-Clause" }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/global": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", @@ -12049,31 +12051,6 @@ "node": ">= 6" } }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "devOptional": true, - "license": "MIT" - }, "node_modules/http-server": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", @@ -12128,29 +12105,6 @@ "node": ">= 6" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -12204,7 +12158,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -12236,7 +12189,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -12285,7 +12237,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -12357,29 +12308,6 @@ "url": "https://opencollective.com/ioredis" } }, - "node_modules/ioredis/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/ioredis/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/ip-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", @@ -12749,7 +12677,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -12895,31 +12822,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", @@ -12935,12 +12837,12 @@ } }, "node_modules/javascript-time-ago": { - "version": "2.5.11", - "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.11.tgz", - "integrity": "sha512-Zeyf5R7oM1fSMW9zsU3YgAYwE0bimEeF54Udn2ixGd8PUwu+z1Yc5t4Y8YScJDMHD6uCx6giLt3VJR5K4CMwbg==", + "version": "2.5.12", + "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.12.tgz", + "integrity": "sha512-s8PPq2HQ3HIbSU0SjhNvTitf5VoXbQWof9q6k3gIX7F2il0ptjD5lONTDccpuKt/2U7RjbCp/TCHPK7eDwO7zQ==", "license": "MIT", "dependencies": { - "relative-time-format": "^1.1.6" + "relative-time-format": "^1.1.7" } }, "node_modules/jest-worker": { @@ -13046,7 +12948,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -13080,7 +12981,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, "license": "MIT" }, "node_modules/json-colorizer": { @@ -13110,14 +13010,12 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, "license": "MIT" }, "node_modules/json5": { @@ -13185,12 +13083,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/jssha": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz", @@ -13258,29 +13150,6 @@ "@types/send": "*" } }, - "node_modules/jwks-rsa/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/jwks-rsa/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/jws": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", @@ -13299,7 +13168,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -13432,7 +13300,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", @@ -13459,221 +13326,6 @@ "license-check-and-add": "dist/src/cli.js" } }, - "node_modules/license-check-and-add/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/license-check-and-add/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/license-check-and-add/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/license-check-and-add/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/license-check-and-add/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "license": "MIT" - }, - "node_modules/license-check-and-add/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/license-check-and-add/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/license-check-and-add/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/license-check-and-add/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/license-check-and-add/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/license-check-and-add/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/license-check-and-add/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, "node_modules/license-headers": { "resolved": "tools/license-headers", "link": true @@ -13724,7 +13376,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -13861,12 +13512,6 @@ "node": ">= 12.0.0" } }, - "node_modules/logform/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -14123,18 +13768,15 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minimist": { @@ -14234,6 +13876,16 @@ "node": ">= 14.0.0" } }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/mocha/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -14246,24 +13898,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/mocha/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -14298,13 +13932,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -14321,6 +13948,34 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -14381,6 +14036,21 @@ "node": ">= 0.8.0" } }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -14403,9 +14073,9 @@ } }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/multer": { @@ -14477,13 +14147,12 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -14669,35 +14338,6 @@ "url": "https://opencollective.com/nodemon" } }, - "node_modules/nodemon/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -14708,26 +14348,6 @@ "node": ">=4" } }, - "node_modules/nodemon/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -14932,13 +14552,6 @@ "node": ">=8" } }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", @@ -15141,7 +14754,6 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", @@ -15171,7 +14783,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "devOptional": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -15187,7 +14798,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -15259,7 +14869,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -15377,7 +14986,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -15396,7 +15004,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -15634,31 +15241,6 @@ "node": ">= 10.12" } }, - "node_modules/portfinder/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -15766,7 +15348,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" @@ -15948,7 +15529,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -16219,28 +15799,6 @@ "node": ">=6.0.0" } }, - "node_modules/recursive-readdir/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -16343,29 +15901,6 @@ "node": ">=8.6.0" } }, - "node_modules/require-in-the-middle/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/require-in-the-middle/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -16427,7 +15962,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -16800,6 +16334,21 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/send/node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -16821,12 +16370,6 @@ "node": ">=4" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -16960,7 +16503,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -16973,7 +16515,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -17143,29 +16684,6 @@ "url": "https://github.com/steveukx/git-js?sponsor=1" } }, - "node_modules/simple-git/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/simple-git/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -17269,12 +16787,6 @@ } } }, - "node_modules/socket.io-adapter/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/socket.io-client": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", @@ -17307,12 +16819,6 @@ } } }, - "node_modules/socket.io-client/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -17343,12 +16849,6 @@ } } }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/socket.io/node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -17366,12 +16866,6 @@ } } }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -17614,7 +17108,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -18019,30 +17512,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -18319,7 +17788,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" @@ -18517,7 +17985,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -18723,31 +18190,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite-node/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/vite-node/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/vitest": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", @@ -18858,24 +18300,6 @@ "node": ">= 16" } }, - "node_modules/vitest/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/vitest/node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -18893,13 +18317,6 @@ "dev": true, "license": "MIT" }, - "node_modules/vitest/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/vitest/node_modules/pathval": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", @@ -19185,7 +18602,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -19344,20 +18760,103 @@ "license": "Apache-2.0" }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/wrappy": { @@ -19480,13 +18979,11 @@ } }, "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" }, "node_modules/yallist": { "version": "4.0.0", @@ -19507,21 +19004,22 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" } }, "node_modules/yargs-parser": { @@ -19576,20 +19074,142 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -19754,6 +19374,16 @@ } } }, + "src/backend-core-0/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "src/backend-core-0/node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -19827,8 +19457,6 @@ }, "src/backend/node_modules/@smithy/abort-controller": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.2.0.tgz", - "integrity": "sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^2.12.0", @@ -19840,8 +19468,6 @@ }, "src/backend/node_modules/@smithy/node-http-handler": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.5.0.tgz", - "integrity": "sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==", "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^2.2.0", @@ -19856,8 +19482,6 @@ }, "src/backend/node_modules/@smithy/protocol-http": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.3.0.tgz", - "integrity": "sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^2.12.0", @@ -19869,8 +19493,6 @@ }, "src/backend/node_modules/@smithy/querystring-builder": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.2.0.tgz", - "integrity": "sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^2.12.0", @@ -19883,8 +19505,6 @@ }, "src/backend/node_modules/@smithy/types": { "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", - "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -19895,8 +19515,6 @@ }, "src/backend/node_modules/@smithy/util-uri-escape": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.2.0.tgz", - "integrity": "sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -19913,6 +19531,20 @@ "undici-types": "~6.21.0" } }, + "src/backend/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "src/backend/node_modules/lru-cache": { "version": "11.0.2", "license": "ISC", @@ -19929,6 +19561,59 @@ "dev": true, "license": "MIT" }, + "src/backend/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "src/backend/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "src/backend/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "src/backend/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "src/emulator": { "version": "1.0.0", "license": "AGPL-3.0-only", @@ -20267,8 +19952,6 @@ }, "src/phoenix/node_modules/@rollup/plugin-commonjs": { "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", - "integrity": "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20293,25 +19976,28 @@ }, "src/phoenix/node_modules/@sinonjs/fake-timers": { "version": "11.3.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", - "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1" } }, + "src/phoenix/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "src/phoenix/node_modules/estree-walker": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, "license": "MIT" }, "src/phoenix/node_modules/glob": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -20330,8 +20016,6 @@ }, "src/phoenix/node_modules/magic-string": { "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", "dev": true, "license": "MIT", "dependencies": { @@ -20343,8 +20027,6 @@ }, "src/phoenix/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -20356,8 +20038,6 @@ }, "src/phoenix/node_modules/rollup": { "version": "3.29.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", - "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, "license": "MIT", "bin": { @@ -20436,8 +20116,6 @@ }, "src/terminal/node_modules/@rollup/plugin-commonjs": { "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", - "integrity": "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==", "dev": true, "license": "MIT", "dependencies": { @@ -20460,18 +20138,23 @@ } } }, + "src/terminal/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "src/terminal/node_modules/estree-walker": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, "license": "MIT" }, "src/terminal/node_modules/glob": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -20490,8 +20173,6 @@ }, "src/terminal/node_modules/magic-string": { "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", "dev": true, "license": "MIT", "dependencies": { @@ -20503,8 +20184,6 @@ }, "src/terminal/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -20516,8 +20195,6 @@ }, "src/terminal/node_modules/rollup": { "version": "3.29.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", - "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index 6163a100b..d710e61e7 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "dotenv": "^16.4.5", "eslint": "^9.35.0", "express": "^4.18.2", - "globals": "^15.0.0", + "globals": "^15.15.0", "html-entities": "^2.3.3", "html-webpack-plugin": "^5.6.0", "husky": "^9.1.7", @@ -54,7 +54,9 @@ "@google/genai": "^1.19.0", "@heyputer/putility": "^1.0.2", "@paralleldrive/cuid2": "^2.2.2", + "@stylistic/eslint-plugin-js": "^4.4.1", "dedent": "^1.5.3", + "express-xml-bodyparser": "^0.4.1", "ioredis": "^5.6.0", "javascript-time-ago": "^2.5.11", "json-colorizer": "^3.0.1", diff --git a/src/backend/src/CoreModule.js b/src/backend/src/CoreModule.js index e01b1be1d..9f83bb849 100644 --- a/src/backend/src/CoreModule.js +++ b/src/backend/src/CoreModule.js @@ -390,7 +390,7 @@ const install = async ({ services, app, useapi, modapi }) => { services.registerService('wisp', WispService); // const { AWSSecretsPopulator } = require('./services/AWSSecretsPopulator.js'); // services.registerService('awsthing', AWSSecretsPopulator); - const { WebDavFS } = require('./services/WebDavFS'); + const { WebDavFS } = require('./services/WebDAV/WebDAVService.js'); services.registerService('dav', WebDavFS); const { RequestMeasureService } = require('./services/RequestMeasureService'); diff --git a/src/backend/src/modules/kvstore/KVStoreInterfaceService.js b/src/backend/src/modules/kvstore/KVStoreInterfaceService.js index 086447034..df181d516 100644 --- a/src/backend/src/modules/kvstore/KVStoreInterfaceService.js +++ b/src/backend/src/modules/kvstore/KVStoreInterfaceService.js @@ -21,15 +21,15 @@ const BaseService = require('../../services/BaseService'); /** * @typedef {Object} KVStoreInterface - * @property {(opts: KVStoreGetParams) => Promise>} get - Retrieve the value(s) for the given key(s). - * @property {(opts: KVStoreSetParams) => Promise} set - Set a value for a key, with optional expiration. - * @property {(opts: KVStoreDelParams) => Promise} del - Delete a value by key. - * @property {(opts: KVStoreListParams) => Promise} list - List all key-value pairs, optionally as a specific type. - * @property {() => Promise} flush - Delete all key-value pairs in the store. - * @property {(opts: KVStoreIncrDecrParams) => Promise} incr - Increment a numeric value by key. - * @property {(opts: KVStoreIncrDecrParams) => Promise} decr - Decrement a numeric value by key. - * @property {(opts: KVStoreExpireAtParams) => Promise} expireAt - Set a key to expire at a specific UNIX timestamp (seconds). - * @property {(opts: KVStoreExpireParams) => Promise} expire - Set a key to expire after a given TTL (seconds). + * @property {function(KVStoreGetParams): Promise>} get - Retrieve the value(s) for the given key(s). + * @property {function(KVStoreSetParams): Promise} set - Set a value for a key, with optional expiration. + * @property {function(KVStoreDelParams): Promise} del - Delete a value by key. + * @property {function(KVStoreListParams): Promise} list - List all key-value pairs, optionally as a specific type. + * @property {function(): Promise} flush - Delete all key-value pairs in the store. + * @property {function(KVStoreIncrDecrParams): Promise} incr - Increment a numeric value by key. + * @property {function(KVStoreIncrDecrParams): Promise} decr - Decrement a numeric value by key. + * @property {function(KVStoreExpireAtParams): Promise} expireAt - Set a key to expire at a specific UNIX timestamp (seconds). + * @property {function(KVStoreExpireParams): Promise} expire - Set a key to expire after a given TTL (seconds). * * @typedef {Object} KVStoreGetParams * @property {string|string[]} key - The key or array of keys to retrieve. @@ -61,8 +61,8 @@ const BaseService = require('../../services/BaseService'); /** * Service for registering the puter-kvstore interface, exposing a simple key-value store API * with support for get, set, delete, list, flush, increment, decrement, and key expiration. -* @extends BaseService -*/ + * @extends BaseService + */ class KVStoreInterfaceService extends BaseService { /** * Service class for managing KVStore interface registrations. diff --git a/src/backend/src/modules/web/WebServerService.js b/src/backend/src/modules/web/WebServerService.js index d9c42a204..57fa09723 100644 --- a/src/backend/src/modules/web/WebServerService.js +++ b/src/backend/src/modules/web/WebServerService.js @@ -80,7 +80,12 @@ class WebServerService extends BaseService { router_webhooks: this.router_webhooks, }); await services.emit('install.routes-gui', { app }); - + + // Register after other services registers theirs: Options for all requests (for CORS) + app.options('/*', (_req, res) => { + return res.sendStatus(200); + }); + this.log.noticeme('web server setup done'); } @@ -664,25 +669,6 @@ class WebServerService extends BaseService { next(); }); - - // Options for all requests (for CORS) - app.options('/*', (req, res) => { - if (req.path.startsWith('/dav/')) { - res.set({ - 'Allow': 'OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, ORDERPATCH', - 'DAV': '1, 2, ordered-collections', // WebDAV compliance classes with ordered-collections for macOS - 'MS-Author-Via': 'DAV', // Microsoft compatibility - 'Server': 'Puter/WebDAV', // Server identification - 'Accept-Ranges': 'bytes', - 'Content-Type': 'text/plain; charset=utf-8', // Explicit content type - 'Content-Length': '0', - 'Cache-Control': 'no-cache', // Prevent caching issues - 'Connection': 'Keep-Alive' // Keep connection alive for macOS - }); - res.status(200).end(); - } - return res.sendStatus(200); - }); } _register_commands (commands) { diff --git a/src/backend/src/services/WebDAV/WebDAVService.js b/src/backend/src/services/WebDAV/WebDAVService.js new file mode 100644 index 000000000..54a4b9c24 --- /dev/null +++ b/src/backend/src/services/WebDAV/WebDAVService.js @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2024-present Puter Technologies Inc. + * + * This file is part of Puter. + * + * Puter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +const { NodePathSelector } = require('../../filesystem/node/selectors'); +const configurable_auth = require('../../middleware/configurable_auth'); +const { Endpoint } = require('../../util/expressutil'); +const BaseService = require('../BaseService'); +const bcrypt = require('bcrypt'); +const xmlparser = require('express-xml-bodyparser'); +let davMethodMap; +let unsupportedMethodHandler; +let COOKIE_NAME = null; + +const ROOT_WEB_DAV_RESPONSE_XML = ` + + + / + + + / + Fri, 03 Jan 2025 10:30:45 GMT + 2025-01-03T10:30:45Z + + "dav-folder-1735898444" + + + + + + + + + + + + 0 + + HTTP/1.1 200 OK + + + + /dav/ + + + dav + Fri, 03 Jan 2025 10:30:45 GMT + 2025-01-03T10:30:45Z + + "dav-folder-1735898445" + + + + + + + + + + + + 0 + + HTTP/1.1 200 OK + + +`; + +class WebDAVService extends BaseService { + async _construct(){ + davMethodMap = (await import ( './methodHandlers/methodMap.mjs')).davMethodMap; + unsupportedMethodHandler = (await import('./methodHandlers/method.mjs')).unsupportedMethodHandler; + } + async _init() { + const svc_web = this.services.get('web-server'); + svc_web.allow_undefined_origin(/^\/dav(\/.*)?$/); + } + #extractHeaderToken = ( headerToken = '' ) => { + let headerLockToken = null; + let prefix = null; + const match = headerToken.match(/(.*)<(urn:uuid:[0-9a-fA-F-]{36})>/); + if ( match ) { + if ( match.length > 2 ) { + headerLockToken = match[2]; + prefix = match[1].trim().slice( 1, -1); // Remove surrounding parentheses + } else { + headerLockToken = match[1]; + } + } + return { headerLockToken, prefix }; + }; + async authenticateWebDavUser( username, password, _req, res ) { + // Default implementation - you should override this method + // Return null to reject authentication + const svc_auth = this.services.get('auth'); + + const user = await this.services + .get('get-user') + .get_user( { username: username, cached: false }); + let otpToken = null; + let real_password = password; + + if ( username === '-token' ) { + return await svc_auth.authenticate_from_token(password); + } + + if ( user.otp_enabled ) { + real_password = password.slice(0, -6); + otpToken = password.slice(-6); + } + + if ( await bcrypt.compare(real_password, user.password) ) { + const { token } = await svc_auth.create_session_token(user); + if ( user.otp_enabled ) { + const svc_otp = this.services.get('otp'); + const ok = svc_otp.verify(user.username, + user.otp_secret, + otpToken); + if ( !ok ) { + return null; + } + } + + res.cookie(COOKIE_NAME, token, { + sameSite: 'none', + secure: true, + httpOnly: true, + maxAge: 34560000000, // 400 days, chrome maximum + }); + return await svc_auth.authenticate_from_token(token); + } + return null; + } + async handleHttpBasicAuth( actor, req, res ) { + if ( actor ) { + return actor; + } + // Check for Basic Authentication header + const authHeader = req.headers.authorization; + if ( authHeader && authHeader.startsWith('Basic ') ) { + try { + // Parse Basic auth credentials + const base64Credentials = authHeader.split(' ')[1]; + const credentials = Buffer.from(base64Credentials, + 'base64').toString( 'ascii'); + let [ username, ...password ] = credentials.split(':'); + password = password.join(':'); + + // Call user's authentication function + actor = await this.authenticateWebDavUser(username, + password, + req, + res); + if ( !actor ) { + // Authentication failed + res.set({ + 'WWW-Authenticate': 'Basic realm="WebDAV"', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + res.status(401).end( 'Unauthorized'); + return; + } else { + return actor; + } + } catch( _e ) { + res.set({ + 'WWW-Authenticate': 'Basic realm="WebDAV"', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + res.status(401).end( 'Unauthorized'); + return; + } + } else { + // No credentials provided, send challenge + res.set({ + 'WWW-Authenticate': 'Basic realm="WebDAV"', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + res.status(401).end( 'Unauthorized'); + return; + } + } + async handleWebDavServer( filePath, req, res ) { + const svc_fs = this.services.get('filesystem'); + const fileNode = await svc_fs.node(new NodePathSelector(filePath)); + // Extract the UUID from the If header (e.g., If: ()) + const ifHeader = req.headers['if']; + const { headerLockToken } = this.#extractHeaderToken(ifHeader); + + const methodHandler = + davMethodMap[req.method] ?? unsupportedMethodHandler; + + methodHandler(req, res, filePath, fileNode, headerLockToken); + } + ['__on_install.routes']( _, { app } ) { + COOKIE_NAME = this.global_config.cookie_name; + + const r_webdav = (() => { + const express = require('express'); + return express.Router(); + } )(); + r_webdav.use(xmlparser()); + + app.use('/dav', r_webdav); + + Endpoint({ + route: '/*', + methods: [ + 'PROPFIND', + 'PROPPATCH', + 'MKCOL', + 'GET', + 'HEAD', + 'POST', + 'PUT', + 'DELETE', + 'COPY', + 'MOVE', + 'LOCK', + 'UNLOCK', + 'OPTIONS', + ], + mw: [ configurable_auth({ optional: true }) ], + /** + * + * @param {import("express").Request} req + * @param {import("express").Response} res + */ + handler: async ( req, res ) => { + const svc_su = this.services.get('su'); + let actor = await this.handleHttpBasicAuth(req.actor, req, res); + if ( !actor ) { + return; + } + let filePath = decodeURIComponent(req.path); + // Handle root path for WebDAV compatibility + if ( filePath === '/' || filePath === '' ) { + filePath = '/'; // Keep as root for WebDAV + } + + svc_su.sudo(actor, async () => { + this.handleWebDavServer(filePath, req, res); + }); + }, + }).attach( r_webdav); + + const r_rootdav = (() => { + const require = this.require; + const express = require('express'); + return express.Router(); + } )(); + app.use('/', r_rootdav); + Endpoint({ + route: '/*', + methods: [ 'PROPFIND' ], + mw: [ configurable_auth({ optional: true }) ], + /** + * + * @param {import("express").Request} req + * @param {import("express").Response} res + */ + handler: async ( req, res ) => { + const svc_su = this.services.get('su'); + + let actor = await this.handleHttpBasicAuth(req.actor, req, res); + if ( !actor ) { + return; + } + + if ( req.path !== '/' && !req.path.startsWith('/dav') ) { + return res.status(404).end( 'Not Found'); + } + if ( req.path === '/dav' ) { + svc_su.sudo(actor, async () => { + this.handleWebDavServer('/', req, res); + }); + } + + // Set proper headers for WebDAV XML response + res.set({ + 'Content-Type': 'application/xml; charset=utf-8', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + + res.status(207); + res.end(ROOT_WEB_DAV_RESPONSE_XML); + }, + }).attach( r_rootdav); + } +} + +module.exports = { + WebDavFS: WebDAVService, +}; diff --git a/src/backend/src/services/WebDAV/lockStore.mjs b/src/backend/src/services/WebDAV/lockStore.mjs new file mode 100644 index 000000000..9cd33f684 --- /dev/null +++ b/src/backend/src/services/WebDAV/lockStore.mjs @@ -0,0 +1,160 @@ +export const DAV_LOCK_DURATION = 30; // seconds + +/* + * @param {string} headerToken + * @returns + */ +export const extractHeaderToken = (headerToken = '') => { + let headerLockToken = null; + let prefix = null; + const match = headerToken.match(/(.*)<(urn:uuid:[0-9a-fA-F-]{36})>/); + if ( match ) { + if ( match.length > 2 ) { + headerLockToken = match[2]; + prefix = match[1].trim().slice( 1, -1); // Remove surrounding parentheses + } else { + headerLockToken = match[1]; + } + } + return { headerLockToken, prefix }; +}; + +const LOCK_PREFIX = 'locktoken:'; +/** + * @param {{sudo:Function}} suService + * @param {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} kvStoreService + * @param {...string} lockTokens + * @returns {Promise<{path: string, lockScope: 'shared' | 'exclusive', lockType?: string}[]>} + */ +export const getLocksIfValid = (suService, kvStoreService, ...lockTokens) => { + return suService.sudo(async () => { + const res = (await kvStoreService.get({ + key: lockTokens.map(lockToken => `${LOCK_PREFIX}${lockToken}`), + })).filter(Boolean); + return res; + }); +}; + +/** + * @param {{sudo:Function}} suService + * @param {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} kvStoreService + * @param {string} filePath + * @param {string} lockScope + * @param {string} lockType + * @returns {Promise} + */ +export const createLock = ( suService, kvStoreService, filePath, lockScope, lockType ) => { + return suService.sudo(async () => { + const lockToken = `urn:uuid:${crypto.randomUUID()}`; + const currentTokens = await getFileLocks(suService, kvStoreService, filePath); + kvStoreService.set({ + key: `${LOCK_PREFIX}${lockToken}`, + value: { path: filePath, lockScope, lockType }, + expireAt: (Date.now() / 1000) + DAV_LOCK_DURATION, + }); + kvStoreService.set({ + key: `${LOCK_PREFIX}${filePath}`, + value: { ...currentTokens, [lockToken]: { lockScope, lockType } }, + expireAt: (Date.now() / 1000) + DAV_LOCK_DURATION, + }); + return lockToken; + }); +}; + +/** + * @param {{sudo:Function}} suService + * @param {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} kvStoreService + * @param {string} lockToken + * @param {string} filePath + * @returns {void} + */ +export const deleteLock = ( suService, kvStoreService, lockToken, filePath ) => { + return suService.sudo(async () => { + kvStoreService.del({ key: `${LOCK_PREFIX}${lockToken}` }); + kvStoreService.del({ key: `${LOCK_PREFIX}${filePath}` }); + }); +}; +/** + * @param {{sudo:Function}} suService + * @param {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} kvStoreService + * @param {string} lockToken + * @param {string} filePath + * @returns + */ +export const refreshLock = ( suService, kvStoreService, lockToken, filePath ) => { + return suService.sudo(async () => { + kvStoreService.expireAt({ + key: `${LOCK_PREFIX}${lockToken}`, + timestamp: (Date.now() / 1000 ) + DAV_LOCK_DURATION, + }); + kvStoreService.expireAt({ + key: `${LOCK_PREFIX}${filePath}`, + timestamp: (Date.now() / 1000 ) + DAV_LOCK_DURATION, + }); + return lockToken; + }); +}; + +/** + * @param {{sudo:Function}} suService + * @param {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} kvStoreService + * @param {string} filePath + * @returns {Promise<{lockToken: string, lockScope: 'shared' | 'exclusive', lockType?: string}[]>} + */ +export const getFileLocks = ( suService, kvStoreService, filePath ) => { + return suService.sudo(async () => { + const parentPaths = filePath.split('/'); + const filePaths = parentPaths.map((_, i, paths) => `${LOCK_PREFIX}${paths.slice(0, i + 1).join('/')}`).filter(Boolean); + const tokenMapList = await kvStoreService.get({ + key: filePaths.slice(2), + }); + return tokenMapList.flatMap(tokenMap => Object.entries(tokenMap ?? {}).map(([ lockToken, lockInfo ]) => ({ + lockToken: lockToken.replace(LOCK_PREFIX, ''), + ...lockInfo, + }))).filter(Boolean); + }); +}; + +/** + * @param {{sudo:Function}} suService + * @param {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} kvStoreService + * @param {string} filePath + * @param {string} headerLockToken + * @returns {Promise} + */ +export const hasWritePermissionInDAV = async ( suService, kvStoreService, filePath, headerLockToken ) => { + + // if no lock on file, allow write + const locksOnFile = await getFileLocks(suService, kvStoreService, filePath); + if ( !locksOnFile?.length ) { + return true; + } + + if ( !headerLockToken ) { + return false; + } + + const existingFileFromLock = (await getLocksIfValid(suService, kvStoreService, headerLockToken))?.pop(); + if ( !filePath.startsWith(existingFileFromLock.path) ) { + return false; + } + + const lock = locksOnFile.find(( l ) => l.lockToken === headerLockToken); + if ( !lock ) { + return false; + } + + if ( lock.lockScope === 'exclusive' ) { + // only 1 exclusive lock can exist, and headerLockToken matches it, allow write + return true; + } + + // if lock(s) on file are shared locks, and headerLockToken is one of them, allow write + if ( lock.lockScope === 'shared' ) { + // this lock should not exist if there are any exclusive locks + return locksOnFile.find(( l ) => l.lockScope === 'exclusive') === undefined; + } + + // else, deny write + return false; +}; \ No newline at end of file diff --git a/src/backend/src/services/WebDAV/methodHandlers/COPY.mjs b/src/backend/src/services/WebDAV/methodHandlers/COPY.mjs new file mode 100644 index 000000000..7917427fe --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/COPY.mjs @@ -0,0 +1,115 @@ +import path from 'path'; +import { NodePathSelector } from '../../../filesystem/node/selectors.js'; +import { hasWritePermissionInDAV } from '../lockStore.mjs'; +import { fsOperations } from '../utils.mjs'; + +/** + * @type {import('./method.mjs').HandlerFunction} + */ +export const COPY = async ( req, res, _filePath, fileNode, headerLockToken ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + + const svc_fs = req.services.get('filesystem'); + const exists = await fileNode?.exists(); + // Check if the resource exists + if ( !exists ) { + res.status(404).end( 'Not Found'); + return; + } + + // Parse Destination header (required for COPY) + const destinationHeader = req.headers.destination; + if ( !destinationHeader ) { + res.status(400).end( 'Bad Request: Destination header required'); + return; + } + + // Parse destination URI - extract path after /dav + let destinationPath; + try { + const destUrl = new URL(destinationHeader, `http://${req.headers.host}`); + if ( !destUrl.pathname.startsWith('/dav/') ) { + res.status(400).end( 'Bad Request: Destination must be within WebDAV namespace'); + return; + } + destinationPath = destUrl.pathname.substring(4); // Remove '/dav' prefix + if ( !destinationPath.startsWith('/') ) { + destinationPath = `/${destinationPath}`; + } + } catch( _e ) { + res.status(400).end( 'Bad Request: Invalid destination URI'); + return; + } + destinationPath = decodeURI(destinationPath); + + // Parse Overwrite header (T = true, F = false, default = T) + const overwriteHeader = req.headers.overwrite; + const overwrite = overwriteHeader !== 'F'; // Default to true unless explicitly F + + // Parse destination path to get parent and new name + const destParentPath = path.dirname(destinationPath); + const destName = path.basename(destinationPath); + + // Check if destination already exists + const destNode = await svc_fs.node(new NodePathSelector(destinationPath)); + const destExists = await destNode.exists(); + + if ( destExists && !overwrite ) { + res.status(412).end( 'Precondition Failed: Destination exists and Overwrite is F'); + return; + } + + // Get destination parent node + const destParentNode = await svc_fs.node(new NodePathSelector(destParentPath)); + const destParentExists = await destParentNode.exists(); + + if ( !destParentExists ) { + res.status(409).end( 'Conflict: Destination parent does not exist'); + return; + } + + // Verify destination parent is a directory + const destParentStat = await fsOperations.stat(destParentNode); + if ( !destParentStat.is_dir ) { + res.status(409).end( 'Conflict: Destination parent is not a directory'); + return; + } + + // check lock + const hasDestinationWriteAccess = await hasWritePermissionInDAV(...servicesForLocks, destinationPath, headerLockToken); + if ( !hasDestinationWriteAccess ) { + // DAV lock in place blocking write to this file + res.status(423).end( 'Locked: No write access to destination'); + } + + // Perform the copy operation + await fsOperations.copy(fileNode, { + destinationNode: destParentNode, + new_name: destName, + overwrite: overwrite, + dedupe_name: false, // WebDAV should not auto-dedupe + }); + + // Set response headers + if ( destExists ) { + res.status(204).end(); // 204 No Content for overwrite + } else { + res.status(201).end(); // 201 Created for new resource + } + } catch( error ) { + // Handle specific error types + if ( error.code === 'permission_denied' ) { + res.status(403).end( 'Forbidden'); + } else if ( error.code === 'item_with_same_name_exists' ) { + res.status(412).end( 'Precondition Failed: Destination exists'); + } else if ( error.code === 'immutable' ) { + res.status(403).end( 'Forbidden: Resource is immutable'); + } else if ( error.code === 'dest_does_not_exist' ) { + res.status(409).end( 'Conflict: Destination parent does not exist'); + } else { + console.error('LOCK error:', error); + res.status(500).end( 'Internal Server Error'); + } + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/DELETE.mjs b/src/backend/src/services/WebDAV/methodHandlers/DELETE.mjs new file mode 100644 index 000000000..448125192 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/DELETE.mjs @@ -0,0 +1,43 @@ +import { hasWritePermissionInDAV } from '../lockStore.mjs'; +import { fsOperations } from '../utils.mjs'; + +/** + * Handler for the DELETE HTTP method in WebDAV. + * @type {import('./method.mjs').HandlerFunction} + */ +export const DELETE = async ( req, res, filePath, fileNode, headerLockToken ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + + const hasDestinationWriteAccess = await hasWritePermissionInDAV(...servicesForLocks, filePath, headerLockToken); + const exists = await fileNode?.exists(); + // Check if the resource exists + if ( !exists ) { + res.status(404).end('Not Found'); + return; + } + + if ( !hasDestinationWriteAccess ){ + // DAV lock in place blocking write to this file + res.status(423).end('Locked: No write access to destination'); + return; + } + // Delete the resource using operations.delete + await fsOperations.delete(fileNode); + + // Return success response + res.status(204).end(); // 204 No Content for successful deletion + } catch( error ) { + // Handle specific error types + if ( error.code === 'permission_denied' ) { + res.status(403).end( 'Forbidden'); + } else if ( error.code === 'immutable' ) { + res.status(403).end( 'Forbidden'); + } else if ( error.code === 'dir_not_empty' ) { + res.status(409).end( 'Conflict'); + } else { + console.error('LOCK error:', error); + res.status(500).end( 'Internal Server Error'); + } + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/HEAD_GET.mjs b/src/backend/src/services/WebDAV/methodHandlers/HEAD_GET.mjs new file mode 100644 index 000000000..d9a701d12 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/HEAD_GET.mjs @@ -0,0 +1,133 @@ +import { fsOperations, getProperMimeType } from '../utils.mjs'; + +const parseRangeHeader = ( rangeHeader ) => { + // Check if this is a multipart range request + if ( rangeHeader.includes(',') ) { + // For now, we'll only serve the first range in multipart requests + // as the underlying storage layer doesn't support multipart responses + const firstRange = rangeHeader.split(',')[0].trim(); + const matches = firstRange.match(/bytes=(\d+)-(\d*)/); + if ( !matches ) { + return null; + } + + const start = parseInt(matches[1], 10); + const end = matches[2] ? parseInt(matches[2], 10) : null; + + return { start, end, isMultipart: true }; + } + + // Single range request + const matches = rangeHeader.match(/bytes=(\d+)-(\d*)/); + if ( !matches ) { + return null; + } + + const start = parseInt(matches[1], 10); + const end = matches[2] ? parseInt(matches[2], 10) : null; + + return { start, end, isMultipart: false }; +}; + +/** + * @type {import('./method.mjs').HandlerFunction} + */ +export const HEAD_GET = async ( req, res, _filePath, fileNode, _headerLockToken ) => { + try { + const exists = await fileNode?.exists(); + if ( !exists ) { + res.status(404).end( 'File not found'); + return; + } + + // Get file stats for Content-Length and other headers + const fileStat = await fsOperations.stat(fileNode); + + // Set appropriate headers + const headers = { + 'Accept-Ranges': 'bytes', + }; + + // Set Content-Length for files (not directories) + if ( !fileStat.is_dir ) { + headers['Content-Length'] = fileStat.size || 0; + headers['Content-Type'] = getProperMimeType(fileStat.type, fileStat.name); + } + + // Set last modified header + if ( fileStat.modified ) { + headers['Last-Modified'] = new Date(fileStat.modified * 1000).toUTCString(); + } + + // Set ETag + headers['ETag'] = `"${fileStat.uid}-${Math.floor(fileStat.modified)}"`; + + res.set(headers); + + // For HEAD requests, only send headers, no body + if ( req.method === 'HEAD' ) { + res.status(200).end(); + return; + } + + // For GET requests, send the file content + if ( fileStat.is_dir ) { + res.status(400).end( 'Cannot GET a directory'); + return; + } + + const options = {}; + + if ( req.headers['range'] ) { + res.status(206); + options.range = req.headers['range']; + // Parse the Range header and set Content-Range + const rangeInfo = parseRangeHeader(req.headers['range']); + if ( rangeInfo ) { + const { start, end, isMultipart } = rangeInfo; + + // For open-ended ranges, we need to calculate the actual end byte + let actualEnd = end; + let fileSize = null; + + try { + fileSize = fileStat.size; + if ( end === null ) { + actualEnd = fileSize - 1; // File size is 1-based, end byte is 0-based + } + } catch( _error ) { + // If we can't get file size, we'll let the storage layer handle it + // and not set Content-Range header + actualEnd = null; + fileSize = null; + } + + if ( actualEnd !== null ) { + const totalSize = fileSize !== null ? fileSize : '*'; + const contentRange = `bytes ${start}-${actualEnd}/${totalSize}`; + res.set('Content-Range', contentRange); + } + + // If this was a multipart request, modify the range header to only include the first range + if ( isMultipart ) { + req.headers['range'] = end !== null ? `bytes=${start}-${end}` : `bytes=${start}-`; + } + } + } + + const stream = await fsOperations.read(fileNode, options); + stream.on('data', ( data ) => { + res.write(data); + }); + stream.on('end', () => { + res.end(); + }); + stream.on('error', ( error ) => { + console.error('Stream error:', error); + res.status(500).end( 'Internal server error'); + }); + } catch( error ) { + console.error('HEAD or GET error:', error); + res.status(500).end( 'Internal Server Error'); + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/LOCK.mjs b/src/backend/src/services/WebDAV/methodHandlers/LOCK.mjs new file mode 100644 index 000000000..c99bacb91 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/LOCK.mjs @@ -0,0 +1,103 @@ +import { createLock, getFileLocks, getLocksIfValid, refreshLock } from '../lockStore.mjs'; +import { escapeXml } from '../utils.mjs'; + +/** + * + * @param {string} lockToken + * @param {string} lockScope + * @param {string} filePath + * @returns + */ +const getLockResponse = ( lockToken, lockScope, filePath ) => { + return ` + + + + + + 0 + + webdav-user + + Second-7200 + + ${lockToken} + + + /dav${escapeXml(encodeURI(filePath))} + + + +`; +}; +/** + * + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {string} filePath + * @param {import('../../../filesystem/FSNodeContext')} fileNode + * @param {string} headerLockToken + * @returns + */ +export const LOCK = async ( req, res, filePath, fileNode, headerLockToken ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + const exists = await fileNode.exists(); + + const lockScope = req.body.lockinfo?.lockscope?.[0]?.shared ? 'shared' : 'exclusive'; + const lockType = req.body.lockinfo?.locktype?.[0]?.write ? 'write' : null; + + const existingFileFromLock = (await getLocksIfValid(...servicesForLocks, headerLockToken)).pop(); + + // Check if the resource exists + if ( !exists ) { + // handle non exsiting child folder if lock is present to refresh parent + if ( existingFileFromLock && filePath.startsWith(existingFileFromLock.path) ) { + filePath = existingFileFromLock.path; + } + // Though technically the resource does not exist, we'll make a lock so that other's can't write to it technically. + } + + const locksOnFile = await getFileLocks(...servicesForLocks, filePath); + // handle exclusive locks if theres any lock in place + if ( + lockScope === 'exclusive' && + locksOnFile?.length && + ( !headerLockToken || existingFileFromLock?.path !== `${filePath}` ) + ) { + res.status(423).end( 'Locked: Resource already locked'); + return; + } + // handle shared locks + if ( + locksOnFile?.length && + locksOnFile?.find(( lock ) => lock.lockScope === '') + && ( + !headerLockToken || existingFileFromLock?.path !== `${filePath}`) + ) { + res.status(423).end( 'Locked: Resource already locked'); + return; + } + + // Generate a UUID lock token + const lockToken = headerLockToken + ? await refreshLock(...servicesForLocks, headerLockToken, filePath) + : await createLock(...servicesForLocks, filePath, lockScope, lockType); + + // Set proper headers for WebDAV XML response + res.set({ + 'Content-Type': 'application/xml; charset=utf-8', + ...( headerLockToken && lockScope !== 'shared' ? {} : { 'Lock-Token': `<${lockToken}>` } ), + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + + // Return lock response + const lockResponse = getLockResponse(lockToken, lockScope, filePath); + res.status(!exists ? 201 : 200); + res.end(lockResponse); + } catch( error ) { + console.error('LOCK error:', error); + res.status(500).end( 'Internal Server Error'); + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/MKCOL.mjs b/src/backend/src/services/WebDAV/methodHandlers/MKCOL.mjs new file mode 100644 index 000000000..c3cfe5c53 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/MKCOL.mjs @@ -0,0 +1,90 @@ +import path from 'path'; +import { NodePathSelector } from '../../../filesystem/node/selectors.js'; +import { hasWritePermissionInDAV } from '../lockStore.mjs'; +import { fsOperations } from '../utils.mjs'; + +/** + * @type {import('./method.mjs').HandlerFunction} + */ +export const MKCOL = async ( req, res, filePath, fileNode, headerLockToken ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + const hasDestinationWriteAccess = await hasWritePermissionInDAV(...servicesForLocks, filePath, headerLockToken); + const exists = await fileNode?.exists(); + // Check if request has a body (not allowed for MKCOL) + const contentLength = req.headers['content-length']; + if ( contentLength && parseInt(contentLength) > 0 ) { + res.status(415).end( 'Unsupported Media Type'); + return; + } + + // Parse the path to get parent directory and target name + const targetPath = filePath; + const parentPath = path.dirname(targetPath); + const targetName = path.basename(targetPath); + + // Handle root directory case + if ( parentPath === '.' || targetPath === '/' ) { + res.status(403).end( 'Forbidden'); + return; + } + + // Check if target already exists + if ( exists ) { + res.status(405).end( 'Method Not Allowed'); + return; + } + + if ( !hasDestinationWriteAccess ){ + // DAV lock in place blocking write to this file + res.status(423).end( 'Locked: No write access to destination'); + return; + } + + // Get parent directory node + const svc_fs = fileNode.services.get('filesystem'); + const parentNode = await svc_fs.node(new NodePathSelector(parentPath)); + const parentExists = await parentNode.exists(); + + if ( !parentExists ) { + res.status(409).end( 'Conflict'); + return; + } + + // Verify parent is a directory + const parentStat = await fsOperations.stat(parentNode); + if ( !parentStat.is_dir ) { + res.status(409).end( 'Conflict'); + return; + } + + // Create the directory + await fsOperations.mkdir(parentNode, { + name: targetName, + overwrite: false, + create_missing_parents: false, + }); + + // Set response headers + res.set({ + Location: `/dav${targetPath}${targetPath.endsWith('/') ? '' : '/'}`, + 'Content-Length': '0', + }); + + res.status(201).end(); // 201 Created + } catch( error ) { + // Handle specific error types + if ( error.code === 'item_with_same_name_exists' ) { + res.status(405).end( 'Method Not Allowed'); + } else if ( error.code === 'permission_denied' ) { + res.status(403).end( 'Forbidden'); + } else if ( error.code === 'dest_does_not_exist' ) { + res.status(409).end( 'Conflict'); + } else if ( error.code === 'invalid_file_name' ) { + res.status(400).end( 'Bad Request'); + } else { + console.error('MKCOL error:', error); + res.status(500).end( 'Internal Server Error'); + } + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/MOVE.mjs b/src/backend/src/services/WebDAV/methodHandlers/MOVE.mjs new file mode 100644 index 000000000..c33ef3d23 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/MOVE.mjs @@ -0,0 +1,118 @@ +import path from 'path'; +import { NodePathSelector } from '../../../filesystem/node/selectors.js'; +import { hasWritePermissionInDAV } from '../lockStore.mjs'; +import { fsOperations } from '../utils.mjs'; + +/** + * MOVE method handler + * @type {import('./method.mjs').HandlerFunction} + */ +export const MOVE = async ( req, res, filePath, fileNode, headerLockToken ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + const hasSourceWriteAccess = await hasWritePermissionInDAV(...servicesForLocks, filePath, headerLockToken); + const svc_fs = req.services.get('filesystem'); + const exists = await fileNode?.exists(); + // Check if the resource exists + if ( !exists ) { + res.status(404).end( 'Not Found'); + return; + } + + // Parse Destination header (required for MOVE) + const destinationHeader = req.headers.destination; + if ( !destinationHeader ) { + res.status(400).end( 'Bad Request: Destination header required'); + return; + } + + // Parse destination URI - extract path after /dav + let destinationPath; + try { + const destUrl = new URL(destinationHeader, `http://${req.headers.host}`); + if ( !destUrl.pathname.startsWith('/dav/') ) { + res.status(400).end( 'Bad Request: Destination must be within WebDAV namespace'); + return; + } + destinationPath = destUrl.pathname.slice(4); // Remove '/dav' prefix + if ( !destinationPath.startsWith('/') ) { + destinationPath = `/${destinationPath}`; + } + } catch { + res.status(400).end( 'Bad Request: Invalid destination URI'); + return; + } + destinationPath = decodeURI(destinationPath); + + const hasDestinationWriteAccess = hasWritePermissionInDAV(destinationPath, headerLockToken); + + // Parse Overwrite header (T = true, F = false, default = T) + const overwriteHeader = req.headers.overwrite; + const overwrite = overwriteHeader !== 'F'; // Default to true unless explicitly F + + // Parse destination path to get parent and new name + const destParentPath = path.dirname(destinationPath); + const destName = path.basename(destinationPath); + + // Check if destination already exists + const destNode = await svc_fs.node(new NodePathSelector(destinationPath)); + const destExists = await destNode.exists(); + + if ( destExists && !overwrite ) { + res.status(412).end( 'Precondition Failed: Destination exists and Overwrite is F'); + return; + } + + // Get destination parent node + const destParentNode = await svc_fs.node(new NodePathSelector(destParentPath)); + const destParentExists = await destParentNode.exists(); + + if ( !destParentExists ) { + res.status(409).end( 'Conflict: Destination parent does not exist'); + return; + } + + // Verify destination parent is a directory + const destParentStat = await fsOperations.stat(destParentNode); + if ( !destParentStat.is_dir ) { + res.status(409).end( 'Conflict: Destination parent is not a directory'); + return; + } + + if ( !hasSourceWriteAccess || !hasDestinationWriteAccess ) { + // DAV lock in place blocking write to this file + res.status(423).end( 'Locked: No write access to source or destination'); + return; + } + + // Perform the move operation + await fsOperations.move(fileNode, { + destinationNode: destParentNode, + new_name: destName, + overwrite: overwrite, + dedupe_name: false, // WebDAV should not auto-dedupe + create_missing_parents: false, + }); + + // Set response headers + if ( destExists ) { + res.status(204).end(); // 204 No Content for overwrite + } else { + res.status(201).end(); // 201 Created for new resource + } + } catch( error ) { + // Handle specific error types + if ( error.code === 'permission_denied' ) { + res.status(403).end( 'Forbidden'); + } else if ( error.code === 'item_with_same_name_exists' ) { + res.status(412).end( 'Precondition Failed: Destination exists'); + } else if ( error.code === 'immutable' ) { + res.status(403).end( 'Forbidden: Resource is immutable'); + } else if ( error.code === 'dest_does_not_exist' ) { + res.status(409).end( 'Conflict: Destination parent does not exist'); + } else { + console.error('LOCK error:', error); + res.status(500).end( 'Internal Server Error'); + } + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/OPTIONS.mjs b/src/backend/src/services/WebDAV/methodHandlers/OPTIONS.mjs new file mode 100644 index 000000000..8e74c78a4 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/OPTIONS.mjs @@ -0,0 +1,14 @@ +export const OPTIONS = async (_req, res) => { + res.set({ + 'Allow': 'OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK', + 'DAV': '1, 2, ordered-collections', // WebDAV compliance classes with ordered-collections for macOS + 'MS-Author-Via': 'DAV', // Microsoft compatibility + 'Server': 'Puter/WebDAV', // Server identification + 'Accept-Ranges': 'bytes', + 'Content-Type': 'text/plain; charset=utf-8', // Explicit content type + 'Content-Length': '0', + 'Cache-Control': 'no-cache', // Prevent caching issues + 'Connection': 'Keep-Alive', // Keep connection alive for macOS + }); + res.status(200).end(); +}; \ No newline at end of file diff --git a/src/backend/src/services/WebDAV/methodHandlers/PROPFIND.mjs b/src/backend/src/services/WebDAV/methodHandlers/PROPFIND.mjs new file mode 100644 index 000000000..8c3403e30 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/PROPFIND.mjs @@ -0,0 +1,177 @@ +import { escapeXml, fsOperations } from '../utils.mjs'; + +const getProperMimeType = ( originalType, filename ) => { + if ( originalType && originalType !== 'application/octet-stream' ) { + return originalType; + } + const ext = filename.split('.').pop()?.toLowerCase(); + switch ( ext ) { + case 'js': + return 'application/javascript'; + case 'css': + return 'text/css'; + case 'html': + case 'htm': + return 'text/html'; + case 'txt': + return 'text/plain'; + case 'json': + return 'application/json'; + case 'xml': + return 'application/xml'; + case 'pdf': + return 'application/pdf'; + case 'png': + return 'image/png'; + case 'jpg': + case 'jpeg': + return 'image/jpeg'; + case 'gif': + return 'image/gif'; + case 'svg': + return 'image/svg+xml'; + default: + return 'application/octet-stream'; + } +}; + +const convertToWebDAVPropfindXML = ( fsEntry ) => { + const isDirectory = fsEntry.is_dir; + const lastModified = new Date(fsEntry.modified * 1000).toUTCString(); + const createdDate = new Date(fsEntry.created * 1000).toISOString(); + let href = fsEntry.path; + if ( isDirectory && !href.endsWith('/') ) { + href += '/'; + } + const xml = ` + + + /dav${escapeXml(encodeURI(href))} + + + ${escapeXml(fsEntry.name)} + ${lastModified} + ${createdDate} + ${ + isDirectory + ? '' + : ` + ${fsEntry.size || 0} + ${escapeXml(getProperMimeType(fsEntry.type, fsEntry.name))}` + } + "${fsEntry.uid}-${Math.floor(fsEntry.modified)}" + + + + + + + + + + + + 0 + + HTTP/1.1 200 OK + + +`; + return xml; +}; + +const convertMultipleToWebDAVPropfindXML = ( selfStat, fsEntries ) => { + fsEntries = [ selfStat, ...fsEntries ]; + const responses = fsEntries + .map(( fsEntry ) => { + const isDirectory = fsEntry.is_dir; + const lastModified = new Date(( fsEntry.modified || 0 ) * 1000).toUTCString(); + const createdDate = new Date(( fsEntry.created || 0 ) * 1000).toISOString(); + let href = fsEntry.path; + if ( isDirectory && !href.endsWith('/') ) { + href += '/'; + } + return ` + /dav${escapeXml(encodeURI(href))} + + + ${escapeXml(fsEntry.name)} + ${lastModified} + ${createdDate} + ${ + isDirectory + ? '' + : ` + ${fsEntry.size || 0} + ${escapeXml(getProperMimeType(fsEntry.type, fsEntry.name))}` + } + "${fsEntry.uid}-${Math.floor(fsEntry.modified)}" + + + + + + + + + + + + 0 + + HTTP/1.1 200 OK + + `; + }) + .join( '\n'); + + return ` + +${responses} +`; +}; + +export const PROPFIND = async ( req, res, filePath, fileNode, _headerLockToken ) => { + try { + res.set({ + 'Content-Type': 'application/xml; charset=utf-8', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + + const exists = await fileNode?.exists(); + + // Handle special case for /dav/ root - return static response with only admin folder + if ( filePath === '/' || filePath === '' ) { + const stat = await fsOperations.stat(fileNode); + const entries = await fsOperations.readdir(fileNode); + res.status(207); + res.end(convertMultipleToWebDAVPropfindXML(stat, entries)); + return; + } + + // Check if file exists + if ( !exists ) { + res.status(404).end( 'Not Found'); + return; + } + + // Handle Depth header (Windows WebDAV client compatibility) + const depth = req.headers.depth || '1'; + + const stat = await fsOperations.stat(fileNode); + + if ( stat.is_dir && depth !== '0' ) { + const entries = await fsOperations.readdir(fileNode); + res.status(207); + res.end(convertMultipleToWebDAVPropfindXML(stat, entries)); + } else { + res.status(207); + res.end(convertToWebDAVPropfindXML(stat)); + } + } catch( error ) { + + console.error('PROPFIND error:', error); + res.status(500).end( 'Internal Server Error'); + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/PROPPATCH.mjs b/src/backend/src/services/WebDAV/methodHandlers/PROPPATCH.mjs new file mode 100644 index 000000000..7ba2373d0 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/PROPPATCH.mjs @@ -0,0 +1,52 @@ +// WebDAV PROPPATCH handler for Puter +import { hasWritePermissionInDAV } from '../lockStore.mjs'; +import { escapeXml } from '../utils.mjs'; + +const getStubResponse = ( filePath ) => ` + + + /dav${escapeXml(encodeURI(filePath))} + + + HTTP/1.1 200 OK + + +`; + +/** + * Handles the WebDAV PROPPATCH method. + * Always returns a generic success response (no extended attributes supported) unless locked, which fails but doesn't matter anyway. + * + * @param {object} req - Express request object + * @param {object} res - Express response object + * @param {string} filePath - Path to the target file + * @param {object} fileNode - File node object (unused in stub) + * @param {string} headerLockToken - Lock token from headers (unused in stub) + */ +export const PROPPATCH = async ( req, res, filePath, _fileNode, headerLockToken ) => { + + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + const hasDestinationWriteAccess = await hasWritePermissionInDAV(...servicesForLocks, filePath, headerLockToken); + + if ( !hasDestinationWriteAccess ) { + // DAV lock in place blocking write to this file + res.status(423).end( 'Locked: No write access to destination'); + return; + } + res.set({ + 'Content-Type': 'application/xml; charset=utf-8', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + // Generic success response (no real property update) + const stubResponse = getStubResponse(filePath); + + res.status(207); + res.end(stubResponse); + } catch( error ) { + // Log error to console (can be replaced with service logger if needed) + console.error('PROPPATCH error:', error); + res.status(500).end( 'Internal Server Error'); + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/PUT.mjs b/src/backend/src/services/WebDAV/methodHandlers/PUT.mjs new file mode 100644 index 000000000..422994c82 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/PUT.mjs @@ -0,0 +1,109 @@ +import path from 'path'; +import { hasWritePermissionInDAV } from '../lockStore.mjs'; +import { fsOperations } from '../utils.mjs'; + +/** + * @type {import('./method.mjs').HandlerFunction} + */ +export const PUT = async ( req, res, filePath, fileNode, headerLockToken ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + const hasDestinationWriteAccess = await hasWritePermissionInDAV(...servicesForLocks, filePath, headerLockToken); + if ( !hasDestinationWriteAccess ){ + // DAV lock in place blocking write to this file + res.status(423).end('Locked: No write access to destination'); + return; + } + // macOS loves polluting webdav directories with metadata which would be stored regularly in HFS+ or APFS. + // We will 422 all of these, because no one actually wants to see them. + const fileName = path.basename(filePath); + if ( + ( req.headers['user-agent'] && + req.headers['user-agent'].includes('Darwin/') && + fileName.toLowerCase() === '.ds_store' ) || + fileName.startsWith('._') + ) { + res.writeHead(422, { + 'Content-Type': 'application/xml; charset=utf-8', + }); + + res.end(` + + macOS metadata files not permitted +`); + return; + } + + // Handle Expect: 100-continue header + if ( req.headers.expect && req.headers.expect.toLowerCase() === '100-continue' ) { + res.writeContinue(); + } + + // Check Content-Length header to find length + // TODO: Allow partial uploads with Range header + // TODO: Allow uploads with no Content-Length + const contentLength = req.headers['content-length'] || req.headers['x-expected-entity-length']; // x-expected-entity-length is used by macOS Finder for some reason + if ( !contentLength ) { + res.status(400).end( 'Content-Length header required'); + return; + } + + const fileSize = parseInt(contentLength); + if ( isNaN(fileSize) || fileSize < 0 ) { + res.status(400).end( 'Invalid Content-Length'); + return; + } + + // Check if file exists before writing (for proper status code) + const existedBefore = await fileNode.exists(); + + // Set Content-Type if provided + const contentType = req.headers['content-type']; + + // Prepare write options + const writeOptions = { + stream: req, // Express request object is a readable stream + size: fileSize, + overwrite: true, // PUT should always overwrite + create_missing_parents: true, // Create directories as needed + no_thumbnail: true, // Disable thumbnails for WebDAV + }; + + // If Content-Type is provided, include it in file metadata + if ( contentType ) { + writeOptions.file = { + mimetype: contentType, + }; + } + + // Write the file + const result = await fsOperations.write(fileNode, writeOptions); + + // Set response headers + res.set({ + ETag: `"${result.uid}-${Math.floor(result.modified)}"`, + 'Last-Modified': new Date(result.modified * 1000).toUTCString(), + }); + + // Return appropriate status code + if ( existedBefore ) { + res.status(204).end(); // 204 No Content for updated file + } else { + res.status(201).end(); // 201 Created for new file + } + } catch( error ) { + // Handle specific error types + if ( error.code === 'item_with_same_name_exists' ) { + res.status(409).end( 'Conflict: Item already exists'); + } else if ( error.code === 'storage_limit_reached' ) { + res.status(507).end( 'Insufficient Storage'); + } else if ( error.code === 'permission_denied' ) { + res.status(403).end( 'Forbidden'); + } else if ( error.code === 'file_too_large' ) { + res.status(413).end( 'Request Entity Too Large'); + } else { + console.error('PUT error:', error); + res.status(500).end( 'Internal Server Error'); + } + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/UNLOCK.mjs b/src/backend/src/services/WebDAV/methodHandlers/UNLOCK.mjs new file mode 100644 index 000000000..403e06d65 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/UNLOCK.mjs @@ -0,0 +1,39 @@ +import { deleteLock, extractHeaderToken, getLocksIfValid } from '../lockStore.mjs'; + +/** + * @type {import('./method.mjs').HandlerFunction} + */ +export const UNLOCK = async ( req, res, filePath, fileNode ) => { + try { + const servicesForLocks = [req.services.get('su'), req.services.get('puter-kvstore').as('puter-kvstore')]; + const exists = await fileNode?.exists(); + // Check if the resource exists + if ( !exists ) { + res.status(204).end(); + return; + } + + // Check for Lock-Token header (normally required for UNLOCK) + const lockTokenHeader = req.headers['lock-token']; + const { headerLockToken } = extractHeaderToken(lockTokenHeader); + + if ( !headerLockToken ) { + res.status(400).end( 'Bad Request: Lock-Token header required'); + return; + } + + const existingFileFromLock = (await getLocksIfValid(...servicesForLocks, headerLockToken)).pop(); + if ( existingFileFromLock ) { + if ( existingFileFromLock.path === filePath ) { + deleteLock(...servicesForLocks, headerLockToken, filePath); + return res.status(204).end(); // 204 No Content for successful unlock + } + return res.status(403).end(); // 403 Forbidden - lock token does not match + } else { + return res.status(409).end(); // 409 Conflict - no lock present + } + } catch( error ) { + console.error('UNLOCK error:', error); + res.status(500).end( 'Internal Server Error'); + } +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/method.mjs b/src/backend/src/services/WebDAV/methodHandlers/method.mjs new file mode 100644 index 000000000..f2a72bf38 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/method.mjs @@ -0,0 +1,27 @@ +/** + * @typedef {import('express').Request & {services: import('../../BaseService.js')}} Request + * @typedef {import('express').Response} Response + * @typedef {import('../../../filesystem/FSNodeContext')} FSNodeContext + */ + +/** + * @typedef {(req: Request, res: Response, filePath: string, fileNode: FSNodeContext, headerLockToken: string) => Promise} HandlerFunction + */ + +/** + * @type {HandlerFunction} + */ +export const unsupportedMethodHandler = async ( + req, + res, + _filePath, + _fileNode, + _headerLockToken ) => { + res.set({ + Allow: + 'OPTIONS, GET, HEAD, POST, PUT, DELETE, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK', + DAV: '1, 2', + 'MS-Author-Via': 'DAV', + }); + res.status(405).end( 'Method Not Allowed'); +}; diff --git a/src/backend/src/services/WebDAV/methodHandlers/methodMap.mjs b/src/backend/src/services/WebDAV/methodHandlers/methodMap.mjs new file mode 100644 index 000000000..a2a7c14f1 --- /dev/null +++ b/src/backend/src/services/WebDAV/methodHandlers/methodMap.mjs @@ -0,0 +1,30 @@ +import { COPY } from './COPY.mjs'; +import { DELETE } from './DELETE.mjs'; +import { HEAD_GET } from './HEAD_GET.mjs'; +import { LOCK } from './LOCK.mjs'; +import { MKCOL } from './MKCOL.mjs'; +import { MOVE } from './MOVE.mjs'; +import { OPTIONS } from './OPTIONS.mjs'; +import { PROPFIND } from './PROPFIND.mjs'; +import { PROPPATCH } from './PROPPATCH.mjs'; +import { PUT } from './PUT.mjs'; +import { UNLOCK } from './UNLOCK.mjs'; + +/** + * Map of HTTP methods to their corresponding handler functions. + * @type {Record} + */ +export const davMethodMap = { + HEAD: HEAD_GET, + GET: HEAD_GET, + LOCK, + UNLOCK, + COPY, + MOVE, + DELETE, + PROPFIND, + PUT, + MKCOL, + PROPPATCH, + OPTIONS, +}; diff --git a/src/backend/src/services/WebDAV/utils.mjs b/src/backend/src/services/WebDAV/utils.mjs new file mode 100644 index 000000000..03b3890dc --- /dev/null +++ b/src/backend/src/services/WebDAV/utils.mjs @@ -0,0 +1,170 @@ +import { HLCopy } from '../../filesystem/hl_operations/hl_copy.js'; +import { HLMkdir } from '../../filesystem/hl_operations/hl_mkdir.js'; +import { HLMove } from '../../filesystem/hl_operations/hl_move.js'; +import { HLReadDir } from '../../filesystem/hl_operations/hl_readdir.js'; +import { HLRemove } from '../../filesystem/hl_operations/hl_remove.js'; +import { HLStat } from '../../filesystem/hl_operations/hl_stat.js'; +import { HLWrite } from '../../filesystem/hl_operations/hl_write.js'; +import { LLRead } from '../../filesystem/ll_operations/ll_read.js'; +import { Context } from '../../util/context.js'; + +/** + * Small utility function to escape XML + * + * @param {string} text + * @returns + */ +export const escapeXml = ( text ) => { + if ( typeof text !== 'string' ) return text; + return text + .replace(/&/g, '&') + .replace( //g, '>') + .replace( /"/g, '"') + .replace( /'/g, '''); +}; + +// Small operations wrapper to make my life a bit easier. Generally it takes a FileNode and returns what puter.fs in puter.js would return. +export const fsOperations = { + stat: ( node ) => { + const hl_stat = new HLStat(); + return hl_stat.run({ + subject: node, + user: Context.get('actor'), + return_subdomains: true, + return_permissions: true, + return_shares: false, + return_versions: false, + return_size: true, + }); + }, + readdir: ( node ) => { + const hl_readdir = new HLReadDir(); + return hl_readdir.run({ + subject: node, + // user: Context.get("actor").type.user, + actor: Context.get('actor'), + recursive: false, + no_thumbs: false, + no_assocs: false, + }); + }, + read: ( node, options ) => { + const ll_read = new LLRead(); + return ll_read.run({ + fsNode: node, + actor: Context.get('actor'), + ...options, + }); + }, + write: ( node, options ) => { + const hl_write = new HLWrite(); + return hl_write.run({ + destination_or_parent: node, + actor: Context.get('actor'), + file: { + stream: options.stream, + size: options.size || 0, + ...options.file, // Allow additional file properties + }, + overwrite: options.overwrite !== undefined ? options.overwrite : true, // Default to true for WebDAV PUT + create_missing_parents: false, + dedupe_name: false, + user: Context.get('actor').type.user, + specified_name: options.name, // Optional filename if node is a directory + fallback_name: options.fallback_name, + shortcut_to: options.shortcut_to, + no_thumbnail: options.no_thumbnail || true, // Disable thumbnails for WebDAV by default + message: options.message, + app_id: options.app_id, + socket_id: options.socket_id, + operation_id: options.operation_id, + item_upload_id: options.item_upload_id, + offset: options.offset, // For partial/resume uploads + }); + }, + mkdir: ( node, options ) => { + const hl_mkdir = new HLMkdir(); + return hl_mkdir.run({ + parent: node, + path: options.path || options.name, // Support both path and name parameters + actor: Context.get('actor'), + overwrite: options.overwrite || false, // WebDAV MKCOL should not overwrite by default + create_missing_parents: + options.create_missing_parents !== undefined ? options.create_missing_parents : true, // Auto-create parent directories + shortcut_to: options.shortcut_to, // Support for shortcuts + user: Context.get('actor').type.user, // User context for permissions + }); + }, + delete: ( node ) => { + const hl_remove = new HLRemove(); + return hl_remove.run({ + target: node, + recursive: true, + user: Context.get('actor'), + }); + }, + move: ( sourceNode, options ) => { + const hl_move = new HLMove(); + return hl_move.run({ + source: sourceNode, // The source fileNode being moved + destination_or_parent: options.destinationNode, // The destination fileNode (could be parent dir or exact destination) + user: Context.get('actor').type.user, + actor: Context.get('actor'), + new_name: options.new_name, // New name in the destination folder + overwrite: options.overwrite !== undefined ? options.overwrite : false, // WebDAV overwrite is optional + dedupe_name: options.dedupe_name || false, // Handle name conflicts + create_missing_parents: options.create_missing_parents || false, // Whether to create missing parent directories + new_metadata: options.new_metadata, // Optional metadata updates + }); + }, + copy: ( sourceNode, options ) => { + const hl_copy = new HLCopy(); + return hl_copy.run({ + source: sourceNode, // The source fileNode being copied + destination_or_parent: options.destinationNode, // The destination fileNode (could be parent dir or exact destination) + user: Context.get('actor').type.user, + new_name: options.new_name, // New name in the destination folder + overwrite: options.overwrite !== undefined ? options.overwrite : false, // WebDAV overwrite is optional + dedupe_name: options.dedupe_name || false, // Handle name conflicts + }); + }, +}; + +export const getProperMimeType = ( originalType, filename ) => { + // If we have a type and it's not the generic octet-stream, use it + if ( originalType && originalType !== 'application/octet-stream' ) { + return originalType; + } + + // Otherwise, guess based on file extension + const ext = filename.split('.').pop()?.toLowerCase(); + switch ( ext ) { + case 'js': + return 'application/javascript'; + case 'css': + return 'text/css'; + case 'html': + case 'htm': + return 'text/html'; + case 'txt': + return 'text/plain'; + case 'json': + return 'application/json'; + case 'xml': + return 'application/xml'; + case 'pdf': + return 'application/pdf'; + case 'png': + return 'image/png'; + case 'jpg': + case 'jpeg': + return 'image/jpeg'; + case 'gif': + return 'image/gif'; + case 'svg': + return 'image/svg+xml'; + default: + return 'application/octet-stream'; + } +}; \ No newline at end of file diff --git a/src/backend/src/services/WebDavFS.js b/src/backend/src/services/WebDavFS.js deleted file mode 100644 index 3817164a1..000000000 --- a/src/backend/src/services/WebDavFS.js +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * Copyright (C) 2024-present Puter Technologies Inc. - * - * This file is part of Puter. - * - * Puter is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - -const { HLReadDir } = require("../filesystem/hl_operations/hl_readdir"); -const { HLStat } = require("../filesystem/hl_operations/hl_stat"); -const { LLRead } = require("../filesystem/ll_operations/ll_read"); -const { HLWrite } = require("../filesystem/hl_operations/hl_write"); -const { HLMkdir } = require("../filesystem/hl_operations/hl_mkdir"); -const { HLMove } = require("../filesystem/hl_operations/hl_move"); -const { HLCopy } = require("../filesystem/hl_operations/hl_copy"); -const { NodePathSelector, NodeUIDSelector } = require("../filesystem/node/selectors"); -const configurable_auth = require("../middleware/configurable_auth"); -const { Context } = require("../util/context"); -const { Endpoint } = require("../util/expressutil"); -const BaseService = require("./BaseService"); -const path = require('path'); -const { HLRemove } = require("../filesystem/hl_operations/hl_remove"); -const bcrypt = require('bcrypt'); - -let COOKIE_NAME = null; - -/** - * Converts a puter fsitem (from stat) to the WebDav PROPFIND equivilent. - * Used for a singlefile PROPFIND. - * - * @param {any} fsEntry - * @returns - */ -function convertToWebDAVPropfindXML(fsEntry) { - const isDirectory = fsEntry.is_dir; - const lastModified = new Date(fsEntry.modified * 1000).toUTCString(); - const createdDate = new Date(fsEntry.created * 1000).toISOString(); - - // Ensure href ends with / for directories - let href = fsEntry.path; - if (isDirectory && !href.endsWith('/')) { - href += '/'; - } - - // Build the XML response - const xml = ` - - - /dav${escapeXml(encodeURI(href))} - - - ${escapeXml(fsEntry.name)} - ${lastModified} - ${createdDate} - ${isDirectory ? - '' : - ` - ${fsEntry.size || 0} - ${escapeXml(getProperMimeType(fsEntry.type, fsEntry.name))}` - } - "${fsEntry.uid}-${Math.floor(fsEntry.modified)}" - - - - - - - - - - - - 0 - - HTTP/1.1 200 OK - - -`; - - return xml; -} - -/** - * Converts a puter fsitem (from readdir) to the WebDav PROPFIND equivilent. - * Used for a directory PROPFIND - * - * @param {any} fsEntry - * @returns - */ -function convertMultipleToWebDAVPropfindXML(selfStat, fsEntries) { - fsEntries = [selfStat, ...fsEntries]; - const responses = fsEntries.map(fsEntry => { - const isDirectory = fsEntry.is_dir; - const lastModified = new Date((fsEntry.modified||0) * 1000).toUTCString(); - const createdDate = new Date((fsEntry.created||0) * 1000).toISOString(); - - // Ensure href ends with / for directories - let href = fsEntry.path; - if (isDirectory && !href.endsWith('/')) { - href += '/'; - } - - return ` - /dav${escapeXml(encodeURI(href))} - - - ${escapeXml(fsEntry.name)} - ${lastModified} - ${createdDate} - ${isDirectory ? - '' : - ` - ${fsEntry.size || 0} - ${escapeXml(getProperMimeType(fsEntry.type, fsEntry.name))}` - } - "${fsEntry.uid}-${Math.floor(fsEntry.modified)}" - - - - - - - - - - - - 0 - - HTTP/1.1 200 OK - - `; - }).join('\n'); - - return ` - -${responses} -`; -} - -function getProperMimeType(originalType, filename) { - // If we have a type and it's not the generic octet-stream, use it - if (originalType && originalType !== 'application/octet-stream') { - return originalType; - } - - // Otherwise, guess based on file extension - const ext = filename.split('.').pop()?.toLowerCase(); - switch (ext) { - case 'js': return 'application/javascript'; - case 'css': return 'text/css'; - case 'html': case 'htm': return 'text/html'; - case 'txt': return 'text/plain'; - case 'json': return 'application/json'; - case 'xml': return 'application/xml'; - case 'pdf': return 'application/pdf'; - case 'png': return 'image/png'; - case 'jpg': case 'jpeg': return 'image/jpeg'; - case 'gif': return 'image/gif'; - case 'svg': return 'image/svg+xml'; - default: return 'application/octet-stream'; - } -} - -/** - * Small utility function to escape XML - * - * @param {string} text - * @returns - */ -function escapeXml(text) { - if (typeof text !== 'string') return text; - return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -const parseRangeHeader = (rangeHeader) => { - // Check if this is a multipart range request - if (rangeHeader.includes(',')) { - // For now, we'll only serve the first range in multipart requests - // as the underlying storage layer doesn't support multipart responses - const firstRange = rangeHeader.split(',')[0].trim(); - const matches = firstRange.match(/bytes=(\d+)-(\d*)/); - if (!matches) return null; - - const start = parseInt(matches[1], 10); - const end = matches[2] ? parseInt(matches[2], 10) : null; - - return { start, end, isMultipart: true }; - } - - // Single range request - const matches = rangeHeader.match(/bytes=(\d+)-(\d*)/); - if (!matches) return null; - - const start = parseInt(matches[1], 10); - const end = matches[2] ? parseInt(matches[2], 10) : null; - - return { start, end, isMultipart: false }; -}; - -function createStaticDavRootResponse() { - const currentDate = new Date().toUTCString(); - const currentISODate = new Date().toISOString(); - const timestamp = Math.floor(Date.now() / 1000); - - return ` - - - /dav/ - - - dav - ${currentDate} - ${currentISODate} - - "dav-root-${timestamp}" - - - - - - - - - - - - 0 - - HTTP/1.1 200 OK - - - - /dav/admin/ - - - admin - ${currentDate} - ${currentISODate} - - "admin-folder-${timestamp}" - - - - - - - - - - - - 0 - - HTTP/1.1 200 OK - - -`; -} - -function createRootWebDAVResponse() { - return ` - - - / - - - / - Fri, 03 Jan 2025 10:30:45 GMT - 2025-01-03T10:30:45Z - - "dav-folder-1735898444" - - - - - - - - - - - - 0 - - HTTP/1.1 200 OK - - - - /dav/ - - - dav - Fri, 03 Jan 2025 10:30:45 GMT - 2025-01-03T10:30:45Z - - "dav-folder-1735898445" - - - - - - - - - - - - 0 - - HTTP/1.1 200 OK - - -`; -} - -// Small operations wrapper to make my life a bit easier. Generally it takes a FileNode and returns what puter.fs in puter.js would return. -const operations = { - stat: (node)=>{ - const hl_stat = new HLStat(); - return hl_stat.run({ - subject: node, - user: Context.get("actor"), - return_subdomains: true, - return_permissions: true, - return_shares: false, - return_versions: false, - return_size: true, - });; - }, - readdir: (node) => { - const hl_readdir = new HLReadDir(); - return hl_readdir.run({ - subject: node, - // user: Context.get("actor").type.user, - actor: Context.get("actor"), - recursive: false, - no_thumbs: false, - no_assocs: false, - }); - }, - read: (node, options) => { - const ll_read = new LLRead(); - return ll_read.run({ - fsNode: node, - actor: Context.get("actor"), - ...options - }); - }, - write: (node, options) => { - const hl_write = new HLWrite(); - return hl_write.run({ - destination_or_parent: node, - actor: Context.get("actor"), - file: { - stream: options.stream, - size: options.size || 0, - ...options.file // Allow additional file properties - }, - overwrite: options.overwrite !== undefined ? options.overwrite : true, // Default to true for WebDAV PUT - create_missing_parents: false, - dedupe_name: false, - user: Context.get("actor").type.user, - specified_name: options.name, // Optional filename if node is a directory - fallback_name: options.fallback_name, - shortcut_to: options.shortcut_to, - no_thumbnail: options.no_thumbnail || true, // Disable thumbnails for WebDAV by default - message: options.message, - app_id: options.app_id, - socket_id: options.socket_id, - operation_id: options.operation_id, - item_upload_id: options.item_upload_id, - offset: options.offset, // For partial/resume uploads - }); - }, - mkdir: (node, options) => { - const hl_mkdir = new HLMkdir(); - return hl_mkdir.run({ - parent: node, - path: options.path || options.name, // Support both path and name parameters - actor: Context.get("actor"), - overwrite: options.overwrite || false, // WebDAV MKCOL should not overwrite by default - create_missing_parents: options.create_missing_parents !== undefined ? options.create_missing_parents : true, // Auto-create parent directories - shortcut_to: options.shortcut_to, // Support for shortcuts - user: Context.get("actor").type.user, // User context for permissions - }); - }, - delete: (node) => { - const hl_remove = new HLRemove(); - return hl_remove.run({ - target: node, - recursive: true, - user: Context.get("actor"), - }); - }, - move: (sourceNode, options) => { - const hl_move = new HLMove(); - return hl_move.run({ - source: sourceNode, // The source fileNode being moved - destination_or_parent: options.destinationNode, // The destination fileNode (could be parent dir or exact destination) - user: Context.get("actor").type.user, - actor: Context.get("actor"), - new_name: options.new_name, // New name in the destination folder - overwrite: options.overwrite !== undefined ? options.overwrite : false, // WebDAV overwrite is optional - dedupe_name: options.dedupe_name || false, // Handle name conflicts - create_missing_parents: options.create_missing_parents || false, // Whether to create missing parent directories - new_metadata: options.new_metadata, // Optional metadata updates - }); - }, - copy: (sourceNode, options) => { - const hl_copy = new HLCopy(); - return hl_copy.run({ - source: sourceNode, // The source fileNode being copied - destination_or_parent: options.destinationNode, // The destination fileNode (could be parent dir or exact destination) - user: Context.get("actor").type.user, - new_name: options.new_name, // New name in the destination folder - overwrite: options.overwrite !== undefined ? options.overwrite : false, // WebDAV overwrite is optional - dedupe_name: options.dedupe_name || false, // Handle name conflicts - }); - } - -} - -/** - * Handles username/password && OTP login. Is used by and wrapped by handleHttpBasicAuth(). - * - * @param {string} username - * @param {string} password - * @param {import("express").Request} req - * @param {import("express").Response} res - * @returns {actor|null} - */ -async function authenticateWebDavUser(username, password, req, res) { - // Default implementation - you should override this method - // Return null to reject authentication - const svc_auth = req.services.get('auth'); - - const user = await req.services.get('get-user').get_user({ username: username, cached: false }); - let otpToken = null; - let real_password = password - - if (username === "-token") { - return await svc_auth.authenticate_from_token(password); - } - - if (user.otp_enabled) { - real_password = password.slice(0, -6); - otpToken = password.slice(-6); - } - - if (await bcrypt.compare(real_password, user.password)) { - const { token } = await svc_auth.create_session_token(user); - if (user.otp_enabled) { - const svc_otp = req.services.get('otp'); - const ok = svc_otp.verify(user.username, user.otp_secret, otpToken); - if (!ok) { - return null; - } - } - - res.cookie(COOKIE_NAME, token, { - sameSite: 'none', - secure: true, - httpOnly: true, - maxAge: 34560000000 // 400 days, chrome maximum - }); - return await svc_auth.authenticate_from_token(token); - } - return null; -} - -/** - * Handler for HTTP BASIC username/password authentication of a puter account. - * It sets a puter token cookie and then returns an actor if it could successfully get one. - * Otherwise, it returns null and responds with an HTTP BASIC authentication request with a 401. - * - * @param {any} actor - * @param {import("express").Request} req - * @param {import("express").Response} res - * @returns {actor|null} - */ -async function handleHttpBasicAuth(actor, req, res) { - - if (actor) - return actor; - // Check for Basic Authentication header - const authHeader = req.headers.authorization; - if (authHeader && authHeader.startsWith('Basic ')) { - try { - // Parse Basic auth credentials - const base64Credentials = authHeader.split(' ')[1]; - const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); - let [username, ...password] = credentials.split(':'); - password = password.join(":"); - - // Call user's authentication function - actor = await authenticateWebDavUser(username, password, req, res); - if (!actor) { - // Authentication failed - res.set({ - 'WWW-Authenticate': 'Basic realm="WebDAV"', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - res.status(401).end('Unauthorized'); - return; - } else { - return actor; - } - } catch (error) { - res.set({ - 'WWW-Authenticate': 'Basic realm="WebDAV"', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - res.status(401).end('Unauthorized'); - return; - } - } else { - // No credentials provided, send challenge - res.set({ - 'WWW-Authenticate': 'Basic realm="WebDAV"', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - res.status(401).end('Unauthorized'); - return; - } -} - -/** - * A full WebDav server in one function. Takes the requested filePath, and an express.js req, res. It responds for you. - * - * @param {string} filePath - * @param {import("express").Request} req - * @param {import("express").Response} res - * @returns - */ - -async function handleWebDavServer(filePath, req, res) { - const svc_fs = this.services.get('filesystem'); - const fileNode = await svc_fs.node(new NodePathSelector(filePath)); - const exists = await fileNode.exists(); - switch (req.method) { - case "GET": - case "HEAD": - if (!exists) { - res.status(404).end('File not found'); - return; - } - - // Get file stats for Content-Length and other headers - const fileStat = await operations.stat(fileNode); - - // Set appropriate headers - const headers = { - 'Accept-Ranges': 'bytes' - }; - - // Set Content-Length for files (not directories) - if (!fileStat.is_dir) { - headers['Content-Length'] = fileStat.size || 0; - headers['Content-Type'] = getProperMimeType(fileStat.type, fileStat.name); - } - - // Set last modified header - if (fileStat.modified) { - headers['Last-Modified'] = new Date(fileStat.modified * 1000).toUTCString(); - } - - // Set ETag - headers['ETag'] = `"${fileStat.uid}-${Math.floor(fileStat.modified)}"`; - - res.set(headers); - - // For HEAD requests, only send headers, no body - if (req.method === "HEAD") { - res.status(200).end(); - break; - } - - // For GET requests, send the file content - if (fileStat.is_dir) { - res.status(400).end('Cannot GET a directory'); - return; - } - - const options = {}; - - if (req.headers["range"]) { - res.status(206); - options.range = req.headers["range"] - // Parse the Range header and set Content-Range - const rangeInfo = parseRangeHeader(req.headers["range"]); - if (rangeInfo) { - const { start, end, isMultipart } = rangeInfo; - - // For open-ended ranges, we need to calculate the actual end byte - let actualEnd = end; - let fileSize = null; - - try { - fileSize = fileStat.size; - if (end === null) { - actualEnd = fileSize - 1; // File size is 1-based, end byte is 0-based - } - } catch (e) { - // If we can't get file size, we'll let the storage layer handle it - // and not set Content-Range header - actualEnd = null; - fileSize = null; - } - - if (actualEnd !== null) { - const totalSize = fileSize !== null ? fileSize : '*'; - const contentRange = `bytes ${start}-${actualEnd}/${totalSize}`; - res.set("Content-Range", contentRange); - } - - // If this was a multipart request, modify the range header to only include the first range - if (isMultipart) { - req.headers["range"] = end !== null - ? `bytes=${start}-${end}` - : `bytes=${start}-`; - } - } - } - - const stream = await operations.read(fileNode, options); - stream.on("data", (data) => { - res.write(data); - }); - stream.on("end", () => { - res.end(); - }); - stream.on("error", (error) => { - console.error("Stream error:", error); - res.status(500).end('Internal server error'); - }); - break; - case "PROPFIND": - // Set proper headers for WebDAV XML response - res.set({ - 'Content-Type': 'application/xml; charset=utf-8', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - - // Handle special case for /dav/ root - return static response with only admin folder - if (filePath === "/" || filePath === "") { - res.status(207); - // res.end(createStaticDavRootResponse()); - const rootNode = await svc_fs.node(new NodePathSelector("/")); - res.end(convertMultipleToWebDAVPropfindXML(await operations.stat(rootNode), await operations.readdir(rootNode))); - return; - } - - if (!exists) { - res.status(404).end('Not Found'); - return; - } - - // Handle Depth header (Windows WebDAV client compatibility) - const depth = req.headers.depth || '1'; - - const stat = await operations.stat(fileNode); - if (stat.is_dir && depth !== '0') { - res.status(207); - res.end(convertMultipleToWebDAVPropfindXML(stat, await operations.readdir(fileNode))); - } else { - res.status(207); - res.end(convertToWebDAVPropfindXML(stat)); - } - break; - case "PUT": - try { - // macOS loves polluting webdav directories with metadata which would be stored regularly in HFS+ or APFS. - // We will 422 all of these, because no one actually wants to see them. - const fileName = path.basename(filePath); - if (req.headers["user-agent"].includes("Darwin/") && fileName.toLowerCase() === ".ds_store" || fileName.startsWith("._")) { - res.writeHead(422, { - 'Content-Type': 'application/xml; charset=utf-8' - }); - - res.end(` - - macOS metadata files not permitted -`); - return; - } - - // Handle Expect: 100-continue header - if (req.headers.expect && req.headers.expect.toLowerCase() === '100-continue') { - res.writeContinue(); - } - - // Check Content-Length header to find length - // TODO: Allow partial uploads with Range header - // TODO: Allow uploads with no Content-Length - const contentLength = req.headers['content-length'] || req.headers['x-expected-entity-length']; // x-expected-entity-length is used by macOS Finder for some reason - if (!contentLength) { - res.status(400).end('Content-Length header required'); - return; - } - - const fileSize = parseInt(contentLength); - if (isNaN(fileSize) || fileSize < 0) { - res.status(400).end('Invalid Content-Length'); - return; - } - - // Check if file exists before writing (for proper status code) - const existedBefore = exists; - - // Set Content-Type if provided - const contentType = req.headers['content-type']; - - // Prepare write options - const writeOptions = { - stream: req, // Express request object is a readable stream - size: fileSize, - overwrite: true, // PUT should always overwrite - create_missing_parents: true, // Create directories as needed - no_thumbnail: true, // Disable thumbnails for WebDAV - }; - - // If Content-Type is provided, include it in file metadata - if (contentType) { - writeOptions.file = { - mimetype: contentType - }; - } - - // Write the file - const result = await operations.write(fileNode, writeOptions); - - // Set response headers - res.set({ - 'ETag': `"${result.uid}-${Math.floor(result.modified)}"`, - 'Last-Modified': new Date(result.modified * 1000).toUTCString() - }); - - // Return appropriate status code - if (existedBefore) { - res.status(204).end(); // 204 No Content for updated file - } else { - res.status(201).end(); // 201 Created for new file - } - } catch (error) { - // Handle specific error types - if (error.code === 'item_with_same_name_exists') { - res.status(409).end('Conflict: Item already exists'); - } else if (error.code === 'storage_limit_reached') { - res.status(507).end('Insufficient Storage'); - } else if (error.code === 'permission_denied') { - res.status(403).end('Forbidden'); - } else if (error.code === 'file_too_large') { - res.status(413).end('Request Entity Too Large'); - } else { - res.status(500).end('Internal Server Error'); - } - } - break; - case "MKCOL": - try { - // Check if request has a body (not allowed for MKCOL) - const contentLength = req.headers['content-length']; - if (contentLength && parseInt(contentLength) > 0) { - res.status(415).end('Unsupported Media Type'); - return; - } - - // Parse the path to get parent directory and target name - const targetPath = filePath; - const parentPath = path.dirname(targetPath); - const targetName = path.basename(targetPath); - - // Handle root directory case - if (parentPath === '.' || targetPath === '/') { - res.status(403).end('Forbidden'); - return; - } - - // Check if target already exists - if (exists) { - res.status(405).end('Method Not Allowed'); - return; - } - - // Get parent directory node - const parentNode = await svc_fs.node(new NodePathSelector(parentPath)); - const parentExists = await parentNode.exists(); - - if (!parentExists) { - res.status(409).end('Conflict'); - return; - } - - // Verify parent is a directory - const parentStat = await operations.stat(parentNode); - if (!parentStat.is_dir) { - res.status(409).end('Conflict'); - return; - } - - // Create the directory - const result = await operations.mkdir(parentNode, { - name: targetName, - overwrite: false, - create_missing_parents: false - }); - - // Set response headers - res.set({ - 'Location': `/dav${targetPath}${targetPath.endsWith('/') ? '' : '/'}`, - 'Content-Length': '0' - }); - - res.status(201).end(); // 201 Created - } catch (error) { - // Handle specific error types - if (error.code === 'item_with_same_name_exists') { - res.status(405).end('Method Not Allowed'); - } else if (error.code === 'permission_denied') { - res.status(403).end('Forbidden'); - } else if (error.code === 'dest_does_not_exist') { - res.status(409).end('Conflict'); - } else if (error.code === 'invalid_file_name') { - res.status(400).end('Bad Request'); - } else { - res.status(500).end('Internal Server Error'); - } - } - break; - case "PROPPATCH": - // Stub implementation for PROPPATCH - always returns success - // Our filesystem doesn't support extended attributes, so we just - // pretend that property updates succeed - try { - // Set proper headers for WebDAV XML response - res.set({ - 'Content-Type': 'application/xml; charset=utf-8', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - - // Return a generic success response - // In a real implementation, we would parse the request body and - // return specific success/failure for each property - const stubResponse = ` - - - /dav${escapeXml(encodeURI(filePath))} - - - HTTP/1.1 200 OK - - -`; - - res.status(207); - res.end(stubResponse); - - } catch (error) { - res.status(500).end('Internal Server Error'); - } - break; - case "DELETE": - try { - // Check if the resource exists - if (!exists) { - res.status(404).end('Not Found'); - return; - } - - // Delete the resource using operations.delete - await operations.delete(fileNode); - - // Return success response - res.status(204).end(); // 204 No Content for successful deletion - } catch (error) { - // Handle specific error types - if (error.code === 'permission_denied') { - res.status(403).end('Forbidden'); - } else if (error.code === 'immutable') { - res.status(403).end('Forbidden'); - } else if (error.code === 'dir_not_empty') { - res.status(409).end('Conflict'); - } else { - res.status(500).end('Internal Server Error'); - } - } - break; - case "MOVE": - try { - // Check if the resource exists - if (!exists) { - res.status(404).end('Not Found'); - return; - } - - // Parse Destination header (required for MOVE) - const destinationHeader = req.headers.destination; - if (!destinationHeader) { - res.status(400).end('Bad Request: Destination header required'); - return; - } - - // Parse destination URI - extract path after /dav - let destinationPath; - try { - const destUrl = new URL(destinationHeader, `http://${req.headers.host}`); - if (!destUrl.pathname.startsWith('/dav/')) { - res.status(400).end('Bad Request: Destination must be within WebDAV namespace'); - return; - } - destinationPath = destUrl.pathname.substring(4); // Remove '/dav' prefix - if (!destinationPath.startsWith('/')) { - destinationPath = '/' + destinationPath; - } - } catch (error) { - res.status(400).end('Bad Request: Invalid destination URI'); - return; - } - destinationPath = decodeURI(destinationPath); - - // Parse Overwrite header (T = true, F = false, default = T) - const overwriteHeader = req.headers.overwrite; - const overwrite = overwriteHeader !== 'F'; // Default to true unless explicitly F - - // Parse destination path to get parent and new name - const destParentPath = path.dirname(destinationPath); - const destName = path.basename(destinationPath); - - // Check if destination already exists - const destNode = await svc_fs.node(new NodePathSelector(destinationPath)); - const destExists = await destNode.exists(); - - if (destExists && !overwrite) { - res.status(412).end('Precondition Failed: Destination exists and Overwrite is F'); - return; - } - - // Get destination parent node - const destParentNode = await svc_fs.node(new NodePathSelector(destParentPath)); - const destParentExists = await destParentNode.exists(); - - if (!destParentExists) { - res.status(409).end('Conflict: Destination parent does not exist'); - return; - } - - // Verify destination parent is a directory - const destParentStat = await operations.stat(destParentNode); - if (!destParentStat.is_dir) { - res.status(409).end('Conflict: Destination parent is not a directory'); - return; - } - - // Perform the move operation - const result = await operations.move(fileNode, { - destinationNode: destParentNode, - new_name: destName, - overwrite: overwrite, - dedupe_name: false, // WebDAV should not auto-dedupe - create_missing_parents: false - }); - - // Set response headers - if (destExists) { - res.status(204).end(); // 204 No Content for overwrite - } else { - res.status(201).end(); // 201 Created for new resource - } - } catch (error) { - // Handle specific error types - if (error.code === 'permission_denied') { - res.status(403).end('Forbidden'); - } else if (error.code === 'item_with_same_name_exists') { - res.status(412).end('Precondition Failed: Destination exists'); - } else if (error.code === 'immutable') { - res.status(403).end('Forbidden: Resource is immutable'); - } else if (error.code === 'dest_does_not_exist') { - res.status(409).end('Conflict: Destination parent does not exist'); - } else { - res.status(500).end('Internal Server Error'); - } - } - break; - case "COPY": - try { - // Check if the resource exists - if (!exists) { - res.status(404).end('Not Found'); - return; - } - - // Parse Destination header (required for COPY) - const destinationHeader = req.headers.destination; - if (!destinationHeader) { - res.status(400).end('Bad Request: Destination header required'); - return; - } - - // Parse destination URI - extract path after /dav - let destinationPath; - try { - const destUrl = new URL(destinationHeader, `http://${req.headers.host}`); - if (!destUrl.pathname.startsWith('/dav/')) { - res.status(400).end('Bad Request: Destination must be within WebDAV namespace'); - return; - } - destinationPath = destUrl.pathname.substring(4); // Remove '/dav' prefix - if (!destinationPath.startsWith('/')) { - destinationPath = '/' + destinationPath; - } - } catch (error) { - res.status(400).end('Bad Request: Invalid destination URI'); - return; - } - destinationPath = decodeURI(destinationPath); - - // Parse Overwrite header (T = true, F = false, default = T) - const overwriteHeader = req.headers.overwrite; - const overwrite = overwriteHeader !== 'F'; // Default to true unless explicitly F - - // Parse destination path to get parent and new name - const destParentPath = path.dirname(destinationPath); - const destName = path.basename(destinationPath); - - // Check if destination already exists - const destNode = await svc_fs.node(new NodePathSelector(destinationPath)); - const destExists = await destNode.exists(); - - if (destExists && !overwrite) { - res.status(412).end('Precondition Failed: Destination exists and Overwrite is F'); - return; - } - - // Get destination parent node - const destParentNode = await svc_fs.node(new NodePathSelector(destParentPath)); - const destParentExists = await destParentNode.exists(); - - if (!destParentExists) { - res.status(409).end('Conflict: Destination parent does not exist'); - return; - } - - // Verify destination parent is a directory - const destParentStat = await operations.stat(destParentNode); - if (!destParentStat.is_dir) { - res.status(409).end('Conflict: Destination parent is not a directory'); - return; - } - - // Perform the copy operation - const result = await operations.copy(fileNode, { - destinationNode: destParentNode, - new_name: destName, - overwrite: overwrite, - dedupe_name: false, // WebDAV should not auto-dedupe - }); - - // Set response headers - if (destExists) { - res.status(204).end(); // 204 No Content for overwrite - } else { - res.status(201).end(); // 201 Created for new resource - } - } catch (error) { - // Handle specific error types - if (error.code === 'permission_denied') { - res.status(403).end('Forbidden'); - } else if (error.code === 'item_with_same_name_exists') { - res.status(412).end('Precondition Failed: Destination exists'); - } else if (error.code === 'immutable') { - res.status(403).end('Forbidden: Resource is immutable'); - } else if (error.code === 'dest_does_not_exist') { - res.status(409).end('Conflict: Destination parent does not exist'); - } else { - res.status(500).end('Internal Server Error'); - } - } - break; - case "LOCK": - // Stub implementation for LOCK - always returns a fake lock token - // Puter doesn't support file locking, so we pretend to lock successfully - try { - // Check if the resource exists - if (!exists) { - res.status(404).end('Not Found'); - return; - } - - // Generate a fake UUID lock token - const lockToken = `urn:uuid:${crypto.randomUUID()}`; - - // Set proper headers for WebDAV XML response - res.set({ - 'Content-Type': 'application/xml; charset=utf-8', - 'Lock-Token': `<${lockToken}>`, - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - - // Return a fake lock response - const lockResponse = ` - - - - - - 0 - - webdav-user - - Second-7200 - - ${lockToken} - - - /dav${escapeXml(encodeURI(filePath))} - - - -`; - - res.status(200); - res.end(lockResponse); - } catch (error) { - res.status(500).end('Internal Server Error'); - } - break; - case "UNLOCK": - // Stub implementation for UNLOCK - always returns success - // Puter doesn't support file locking, so we pretend to unlock successfully - try { - // Check if the resource exists - if (!exists) { - res.status(404).end('Not Found'); - return; - } - - // Check for Lock-Token header (normally required for UNLOCK) - const lockToken = req.headers['lock-token']; - if (!lockToken) { - res.status(400).end('Bad Request: Lock-Token header required'); - return; - } - - // Always return success since we don't actually track locks - res.status(204).end(); // 204 No Content for successful unlock - } catch (error) { - res.status(500).end('Internal Server Error'); - } - break; - default: - // Method not allowed - res.set({ - 'Allow': 'OPTIONS, GET, HEAD, POST, PUT, DELETE, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - res.status(405).end('Method Not Allowed'); - break; - } -} - - -class WebDavFS extends BaseService { - async _init() { - - const svc_web = this.services.get('web-server'); - svc_web.allow_undefined_origin(/^\/dav(\/.*)?$/);; - - } - - ['__on_install.routes'](_, { app }) { - COOKIE_NAME = this.global_config.cookie_name - - const r_webdav = (() => { - const require = this.require; - const express = require('express'); - return express.Router(); - })(); - - app.use('/dav', r_webdav); - - Endpoint({ - route: '/*', - methods: ["PROPFIND", "PROPPATCH", "MKCOL", "GET", "HEAD", "POST", "PUT", "DELETE", "COPY", "MOVE", "LOCK", "UNLOCK"], - mw: [configurable_auth({ optional: true })], - /** - * - * @param {import("express").Request} req - * @param {import("express").Response} res - */ - handler: async (req, res) => { - const svc_su = this.services.get("su") - let actor = await handleHttpBasicAuth(req.actor, req, res); - if (!actor) return; - let filePath = decodeURIComponent(req.path) - // Handle root path for WebDAV compatibility - if (filePath === "/" || filePath === "") { - filePath = "/"; // Keep as root for WebDAV - } - - svc_su.sudo(actor, async ()=> { - handleWebDavServer(filePath, req, res); - }) - - } - - }).attach(r_webdav); - - const r_rootdav = (() => { - const require = this.require; - const express = require('express'); - return express.Router(); - })(); - app.use('/', r_rootdav); - Endpoint({ - route: "/*", - methods: ["PROPFIND"], - mw: [configurable_auth({ optional: true })], - /** - * - * @param {import("express").Request} req - * @param {import("express").Response} res - */ - handler: async (req, res) => { - const svc_su = this.services.get("su"); - - let actor = await handleHttpBasicAuth(req.actor, req, res); - if (!actor) return; - - if (req.path !== "/" && !req.path.startsWith("/dav")) { - return res.status(404).end('Not Found'); - } - if (req.path === "/dav") { - svc_su.sudo(actor, async () => { - handleWebDavServer("/", req, res); - }) - } - - // Set proper headers for WebDAV XML response - res.set({ - 'Content-Type': 'application/xml; charset=utf-8', - 'DAV': '1, 2', - 'MS-Author-Via': 'DAV' - }); - - res.status(207); - res.end(createRootWebDAVResponse()); - - } - - }).attach(r_rootdav); - } -} - -module.exports = { - WebDavFS, -}; diff --git a/src/puter-js/src/modules/KV.js b/src/puter-js/src/modules/KV.js index 7aa83e827..f65319a5a 100644 --- a/src/puter-js/src/modules/KV.js +++ b/src/puter-js/src/modules/KV.js @@ -19,7 +19,7 @@ const gui_cache_keys = [ ]; class KV{ MAX_KEY_SIZE = 1024; - MAX_VALUE_SIZE = 400 * 1024; + MAX_VALUE_SIZE = 399 * 1024; /** * Creates a new instance with the given authentication token, API origin, and app ID, @@ -50,7 +50,7 @@ class KV{ args: { key: gui_cache_keys, }, - auth_token: this.authToken + auth_token: this.authToken, }), }); const arr_values = await resp.json(); @@ -95,14 +95,25 @@ class KV{ } /** - * Resolves to 'true' on success, or rejects with an error on failure - * - * `key` cannot be undefined or null. - * `key` size cannot be larger than 1mb. - * `value` size cannot be larger than 10mb. - * `expireAt` is a timestamp in sec since epoch. If provided, the key will expire at the given time. + * @typedef {function(key: string, value: any, expireAt?: number): Promise} SetFunction + * Resolves to 'true' on success, or rejects with an error on failure. + * @param {string} key - Cannot be undefined or null. Cannot be larger than 1KB. + * @param {any} value - Cannot be larger than 399KB. + * @param {number} [expireAt] - Optional expiration time for the key. Note that clients with a clock that is not in sync with the server may experience issues with this method. + * @memberof KV */ + + /** @type {SetFunction} */ set = utils.make_driver_method(['key', 'value', 'expireAt'], 'puter-kvstore', undefined, 'set', { + /** + * + * @param {object} args + * @param {string} args.key + * @param {any} args.value + * @param {number} [args.expireAt] + * @memberof [KV] + * @returns + */ preprocess: (args) => { // key cannot be undefined or null if ( args.key === undefined || args.key === null ){ @@ -110,11 +121,11 @@ class KV{ } // key size cannot be larger than MAX_KEY_SIZE if ( args.key.length > this.MAX_KEY_SIZE ){ - throw { message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }; + throw { message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }; } // value size cannot be larger than MAX_VALUE_SIZE if ( args.value && args.value.length > this.MAX_VALUE_SIZE ){ - throw { message: 'Value size cannot be larger than ' + this.MAX_VALUE_SIZE, code: 'value_too_large' }; + throw { message: `Value size cannot be larger than ${this.MAX_VALUE_SIZE}`, code: 'value_too_large' }; } return args; }, @@ -143,7 +154,7 @@ class KV{ preprocess: (args) => { // key size cannot be larger than MAX_KEY_SIZE if ( args.key.length > this.MAX_KEY_SIZE ){ - throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }); + throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }); } return args; @@ -166,7 +177,7 @@ class KV{ // key size cannot be larger than MAX_KEY_SIZE if ( options.key.length > this.MAX_KEY_SIZE ){ - throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }); + throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }); } return utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'incr').call(this, options); @@ -185,33 +196,49 @@ class KV{ // key size cannot be larger than MAX_KEY_SIZE if ( options.key.length > this.MAX_KEY_SIZE ){ - throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }); + throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }); } return utils.make_driver_method(['key'], 'puter-kvstore', undefined, 'decr').call(this, options); }; - expire = async (...args) => { + /** + * Set a time to live (in seconds) on a key. After the time to live has expired, the key will be deleted. + * Prefer this over expireAt if you want timestamp to be set by the server, to avoid issues with clock drift. + * @param {string} key - The key to set the expiration on. + * @param {number} ttl - The ttl + * @memberof [KV] + * @returns + */ + expire = async (key, ttl) => { let options = {}; - options.key = args[0]; - options.ttl = args[1]; + options.key = key; + options.ttl = ttl; // key size cannot be larger than MAX_KEY_SIZE if ( options.key.length > this.MAX_KEY_SIZE ){ - throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }); + throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }); } return utils.make_driver_method(['key', 'ttl'], 'puter-kvstore', undefined, 'expire').call(this, options); }; - expireAt = async (...args) => { + /** + * + * Set the expiration for a key as a UNIX timestamp (in seconds). After the time has passed, the key will be deleted. + * Note that clients with a clock that is not in sync with the server may experience issues with this method. + * @param {string} key - The key to set the expiration on. + * @param {number} timestamp - The timestamp (in seconds since epoch) when the key will expire. + * @memberof [KV] + * @returns + */ + expireAt = async (key, timestamp) => { let options = {}; - options.key = args[0]; - options.timestamp = args[1]; - + options.key = key; + options.timestamp = timestamp; // key size cannot be larger than MAX_KEY_SIZE if ( options.key.length > this.MAX_KEY_SIZE ){ - throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }); + throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }); } return utils.make_driver_method(['key', 'timestamp'], 'puter-kvstore', undefined, 'expireAt').call(this, options); @@ -223,7 +250,7 @@ class KV{ preprocess: (args) => { // key size cannot be larger than this.MAX_KEY_SIZE if ( args.key.length > this.MAX_KEY_SIZE ){ - throw ({ message: 'Key size cannot be larger than ' + this.MAX_KEY_SIZE, code: 'key_too_large' }); + throw ({ message: `Key size cannot be larger than ${this.MAX_KEY_SIZE}`, code: 'key_too_large' }); } return args; @@ -292,7 +319,7 @@ function globMatch(pattern, str) { .replace(/\\\]/g, ']') // Replace ] with ] .replace(/\\\^/g, '^'); // Replace ^ with ^ - let re = new RegExp('^' + regexPattern + '$'); + let re = new RegExp(`^${regexPattern}$`); return re.test(str); }