1 Commits

Author SHA1 Message Date
Sebastian fae4ee8929 fix(deps): update dependency symfony/console to v8
JS Unit Tests / test (pull_request) Successful in 21s
Build Test / build (pull_request) Successful in 25s
PHP Unit Tests / test (pull_request) Successful in 1m46s
2026-05-17 03:03:00 +00:00
166 changed files with 1346 additions and 1983 deletions
Generated
+17 -27
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "95b9110e6c06e2d30edd0283b1bd630d",
"content-hash": "f7fc2a065e49b48ce405b1a490da44af",
"packages": [
{
"name": "laravel/serializable-closure",
@@ -393,16 +393,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.55",
"version": "3.0.52",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "db9744e6d47e742b1f974e965ad49bdd041105af"
"reference": "2adaefc83df2ec548558307690f376dd7d4f4fce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db9744e6d47e742b1f974e965ad49bdd041105af",
"reference": "db9744e6d47e742b1f974e965ad49bdd041105af",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/2adaefc83df2ec548558307690f376dd7d4f4fce",
"reference": "2adaefc83df2ec548558307690f376dd7d4f4fce",
"shasum": ""
},
"require": {
@@ -483,7 +483,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.55"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.52"
},
"funding": [
{
@@ -499,7 +499,7 @@
"type": "tidelift"
}
],
"time": "2026-06-14T23:24:10+00:00"
"time": "2026-04-27T07:02:15+00:00"
},
{
"name": "psr/container",
@@ -606,29 +606,23 @@
},
{
"name": "symfony/console",
"version": "v8.1.1",
"version": "v8.0.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "b711a8ab808b6c074c6b8caef70d0fd8d6b6d07d"
"reference": "3156577f46a38aa1b9323aad223de7a9cd426782"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/b711a8ab808b6c074c6b8caef70d0fd8d6b6d07d",
"reference": "b711a8ab808b6c074c6b8caef70d0fd8d6b6d07d",
"url": "https://api.github.com/repos/symfony/console/zipball/3156577f46a38aa1b9323aad223de7a9cd426782",
"reference": "3156577f46a38aa1b9323aad223de7a9cd426782",
"shasum": ""
},
"require": {
"php": ">=8.4.1",
"symfony/deprecation-contracts": "^2.5|^3",
"php": ">=8.4",
"symfony/polyfill-mbstring": "^1.0",
"symfony/polyfill-php85": "^1.32",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^7.4.6|^8.0.6"
},
"conflict": {
"symfony/dependency-injection": "<8.1",
"symfony/event-dispatcher": "<8.1"
"symfony/string": "^7.4|^8.0"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
@@ -636,18 +630,14 @@
"require-dev": {
"psr/log": "^1|^2|^3",
"symfony/config": "^7.4|^8.0",
"symfony/dependency-injection": "^8.1",
"symfony/event-dispatcher": "^8.1",
"symfony/filesystem": "^7.4|^8.0",
"symfony/dependency-injection": "^7.4|^8.0",
"symfony/event-dispatcher": "^7.4|^8.0",
"symfony/http-foundation": "^7.4|^8.0",
"symfony/http-kernel": "^7.4|^8.0",
"symfony/lock": "^7.4|^8.0",
"symfony/messenger": "^7.4|^8.0",
"symfony/mime": "^7.4|^8.0",
"symfony/process": "^7.4|^8.0",
"symfony/stopwatch": "^7.4|^8.0",
"symfony/uid": "^7.4|^8.0",
"symfony/validator": "^7.4|^8.0",
"symfony/var-dumper": "^7.4|^8.0"
},
"type": "library",
@@ -682,7 +672,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v8.1.1"
"source": "https://github.com/symfony/console/tree/v8.0.11"
},
"funding": [
{
@@ -702,7 +692,7 @@
"type": "tidelift"
}
],
"time": "2026-06-16T12:55:20+00:00"
"time": "2026-05-13T12:07:53+00:00"
},
{
"name": "symfony/deprecation-contracts",
+143 -143
View File
@@ -747,9 +747,9 @@
"license": "MIT"
},
"node_modules/@oxc-project/types": {
"version": "0.132.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz",
"integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==",
"version": "0.130.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz",
"integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
@@ -1083,9 +1083,9 @@
"license": "MIT"
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz",
"integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz",
"integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==",
"cpu": [
"arm64"
],
@@ -1099,9 +1099,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz",
"integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz",
"integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==",
"cpu": [
"arm64"
],
@@ -1115,9 +1115,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz",
"integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz",
"integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==",
"cpu": [
"x64"
],
@@ -1131,9 +1131,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz",
"integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz",
"integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==",
"cpu": [
"x64"
],
@@ -1147,9 +1147,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz",
"integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz",
"integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==",
"cpu": [
"arm"
],
@@ -1163,9 +1163,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz",
"integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz",
"integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==",
"cpu": [
"arm64"
],
@@ -1179,9 +1179,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz",
"integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz",
"integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==",
"cpu": [
"arm64"
],
@@ -1195,9 +1195,9 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz",
"integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz",
"integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==",
"cpu": [
"ppc64"
],
@@ -1211,9 +1211,9 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz",
"integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz",
"integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==",
"cpu": [
"s390x"
],
@@ -1227,9 +1227,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz",
"integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz",
"integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==",
"cpu": [
"x64"
],
@@ -1243,9 +1243,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz",
"integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz",
"integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==",
"cpu": [
"x64"
],
@@ -1259,9 +1259,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz",
"integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz",
"integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==",
"cpu": [
"arm64"
],
@@ -1275,9 +1275,9 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz",
"integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz",
"integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==",
"cpu": [
"wasm32"
],
@@ -1293,9 +1293,9 @@
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz",
"integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz",
"integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==",
"cpu": [
"arm64"
],
@@ -1309,9 +1309,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz",
"integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz",
"integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==",
"cpu": [
"x64"
],
@@ -1688,14 +1688,14 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.7.tgz",
"integrity": "sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.6.tgz",
"integrity": "sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
"@vitest/utils": "4.1.7",
"@vitest/utils": "4.1.6",
"ast-v8-to-istanbul": "^1.0.0",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
@@ -1709,8 +1709,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "4.1.7",
"vitest": "4.1.7"
"@vitest/browser": "4.1.6",
"vitest": "4.1.6"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -1719,16 +1719,16 @@
}
},
"node_modules/@vitest/expect": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz",
"integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz",
"integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.1.7",
"@vitest/utils": "4.1.7",
"@vitest/spy": "4.1.6",
"@vitest/utils": "4.1.6",
"chai": "^6.2.2",
"tinyrainbow": "^3.1.0"
},
@@ -1737,13 +1737,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz",
"integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz",
"integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.1.7",
"@vitest/spy": "4.1.6",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -1774,9 +1774,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz",
"integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz",
"integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1787,13 +1787,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz",
"integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz",
"integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.1.7",
"@vitest/utils": "4.1.6",
"pathe": "^2.0.3"
},
"funding": {
@@ -1801,14 +1801,14 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz",
"integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz",
"integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.7",
"@vitest/utils": "4.1.7",
"@vitest/pretty-format": "4.1.6",
"@vitest/utils": "4.1.6",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -1817,9 +1817,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz",
"integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz",
"integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -1827,13 +1827,13 @@
}
},
"node_modules/@vitest/ui": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.7.tgz",
"integrity": "sha512-TP6utB2yX6rsJNVRo2qAlsi48i1YwFTrLV2tnTtWqJaYX7m4lRCCLirZBjU6xC5m0RsPHr+L2+N+eIPhgEzFfw==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.6.tgz",
"integrity": "sha512-wiu5em68DfGv/2HFvI1Njr7JI2CHcBlQvereSzVG8my53PRxjTNOCsD9VOkRKrsJBDHmyuXvosxWZw7T91a2mw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.1.7",
"@vitest/utils": "4.1.6",
"fflate": "^0.8.2",
"flatted": "^3.4.2",
"pathe": "^2.0.3",
@@ -1845,17 +1845,17 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"vitest": "4.1.7"
"vitest": "4.1.6"
}
},
"node_modules/@vitest/utils": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz",
"integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz",
"integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.1.7",
"@vitest/pretty-format": "4.1.6",
"convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0"
},
@@ -2084,9 +2084,9 @@
"license": "MIT"
},
"node_modules/@vue/test-utils": {
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.11.tgz",
"integrity": "sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==",
"version": "2.4.10",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.10.tgz",
"integrity": "sha512-SmoZ5EA1kYiAFs9NkYdiFFQF+cSnUwnvlYEbY+DogWQZUiqOm/Y29eSbc5T6yi75SgSF9863SBeXniIEoPajCA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2944,9 +2944,9 @@
}
},
"node_modules/dompurify": {
"version": "3.4.11",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
"integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.3.tgz",
"integrity": "sha512-VVwJidIJcp1hpg2OMXML3ZVRPYSZiq4aX7qBh83BSIpOaRDqI+qxhXjjIWnpzkOXhmp0L81lnoME1mnCc9H48A==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -3206,9 +3206,9 @@
}
},
"node_modules/eslint-plugin-vue": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.2.tgz",
"integrity": "sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==",
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.1.tgz",
"integrity": "sha512-cHB0Tf4Duvzwecwd/AqWzZvF/QszE13BhjVUpVXWCy9AeMR5GjkAjP3i85vqgLgOuTmkHR1OJ5oMeqLHtuw8zg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4900,9 +4900,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"funding": [
{
"type": "opencollective",
@@ -4919,7 +4919,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.12",
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -4951,9 +4951,9 @@
}
},
"node_modules/prettier": {
"version": "3.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
"integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -5076,12 +5076,12 @@
"license": "MIT"
},
"node_modules/rolldown": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz",
"integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz",
"integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==",
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.132.0",
"@oxc-project/types": "=0.130.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
@@ -5091,21 +5091,21 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.2",
"@rolldown/binding-darwin-arm64": "1.0.2",
"@rolldown/binding-darwin-x64": "1.0.2",
"@rolldown/binding-freebsd-x64": "1.0.2",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.2",
"@rolldown/binding-linux-arm64-gnu": "1.0.2",
"@rolldown/binding-linux-arm64-musl": "1.0.2",
"@rolldown/binding-linux-ppc64-gnu": "1.0.2",
"@rolldown/binding-linux-s390x-gnu": "1.0.2",
"@rolldown/binding-linux-x64-gnu": "1.0.2",
"@rolldown/binding-linux-x64-musl": "1.0.2",
"@rolldown/binding-openharmony-arm64": "1.0.2",
"@rolldown/binding-wasm32-wasi": "1.0.2",
"@rolldown/binding-win32-arm64-msvc": "1.0.2",
"@rolldown/binding-win32-x64-msvc": "1.0.2"
"@rolldown/binding-android-arm64": "1.0.1",
"@rolldown/binding-darwin-arm64": "1.0.1",
"@rolldown/binding-darwin-x64": "1.0.1",
"@rolldown/binding-freebsd-x64": "1.0.1",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.1",
"@rolldown/binding-linux-arm64-gnu": "1.0.1",
"@rolldown/binding-linux-arm64-musl": "1.0.1",
"@rolldown/binding-linux-ppc64-gnu": "1.0.1",
"@rolldown/binding-linux-s390x-gnu": "1.0.1",
"@rolldown/binding-linux-x64-gnu": "1.0.1",
"@rolldown/binding-linux-x64-musl": "1.0.1",
"@rolldown/binding-openharmony-arm64": "1.0.1",
"@rolldown/binding-wasm32-wasi": "1.0.1",
"@rolldown/binding-win32-arm64-msvc": "1.0.1",
"@rolldown/binding-win32-x64-msvc": "1.0.1"
}
},
"node_modules/sass": {
@@ -6050,15 +6050,15 @@
}
},
"node_modules/vite": {
"version": "8.0.14",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
"integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==",
"version": "8.0.13",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz",
"integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==",
"license": "MIT",
"dependencies": {
"lightningcss": "^1.32.0",
"picomatch": "^4.0.4",
"postcss": "^8.5.15",
"rolldown": "1.0.2",
"postcss": "^8.5.14",
"rolldown": "1.0.1",
"tinyglobby": "^0.2.16"
},
"bin": {
@@ -6233,19 +6233,19 @@
}
},
"node_modules/vitest": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz",
"integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz",
"integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "4.1.7",
"@vitest/mocker": "4.1.7",
"@vitest/pretty-format": "4.1.7",
"@vitest/runner": "4.1.7",
"@vitest/snapshot": "4.1.7",
"@vitest/spy": "4.1.7",
"@vitest/utils": "4.1.7",
"@vitest/expect": "4.1.6",
"@vitest/mocker": "4.1.6",
"@vitest/pretty-format": "4.1.6",
"@vitest/runner": "4.1.6",
"@vitest/snapshot": "4.1.6",
"@vitest/spy": "4.1.6",
"@vitest/utils": "4.1.6",
"es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0",
"magic-string": "^0.30.21",
@@ -6273,12 +6273,12 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.1.7",
"@vitest/browser-preview": "4.1.7",
"@vitest/browser-webdriverio": "4.1.7",
"@vitest/coverage-istanbul": "4.1.7",
"@vitest/coverage-v8": "4.1.7",
"@vitest/ui": "4.1.7",
"@vitest/browser-playwright": "4.1.6",
"@vitest/browser-preview": "4.1.6",
"@vitest/browser-webdriverio": "4.1.6",
"@vitest/coverage-istanbul": "4.1.6",
"@vitest/coverage-v8": "4.1.6",
"@vitest/ui": "4.1.6",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -40,10 +40,6 @@ abstract class CollectionMutableAbstract extends NodeMutableAbstract implements
// Copy all property values
$this->properties->setLabel($value->getLabel());
$this->properties->setDescription($value->getDescription());
$this->properties->setRank($value->getRank());
$this->properties->setVisibility($value->getVisibility());
$this->properties->setColor($value->getColor());
return $this;
}
@@ -20,53 +20,57 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
*/
abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements CollectionPropertiesBaseInterface {
public const JSON_TYPE = CollectionPropertiesBaseInterface::JSON_TYPE;
/**
* @inheritDoc
*/
public function content(): array {
$content = $this->data[self::PROPERTY_CONTENT] ?? null;
if (is_array($content)) {
return array_filter(array_map(function ($item) {
return $item instanceof CollectionContent ? $item : CollectionContent::tryFrom($item);
}, $content));
} else {
return [];
public function content(): CollectionContent {
$content = $this->data[self::JSON_PROPERTY_CONTENTS] ?? null;
if ($content instanceof CollectionContent) {
return $content;
}
if (is_string($content)) {
return CollectionContent::tryFrom($content) ?? CollectionContent::Event;
}
return CollectionContent::Event;
}
/**
* @inheritDoc
*/
public function getLabel(): string {
return $this->data[self::PROPERTY_LABEL] ?? '';
return $this->data[self::JSON_PROPERTY_LABEL] ?? '';
}
/**
* @inheritDoc
*/
public function getDescription(): ?string {
return $this->data[self::PROPERTY_DESCRIPTION] ?? null;
return $this->data[self::JSON_PROPERTY_DESCRIPTION] ?? null;
}
/**
* @inheritDoc
*/
public function getRank(): ?int {
return $this->data[self::PROPERTY_RANK] ?? null;
public function getPriority(): ?int {
return $this->data[self::JSON_PROPERTY_PRIORITY] ?? null;
}
/**
* @inheritDoc
*/
public function getVisibility(): ?bool {
return $this->data[self::PROPERTY_VISIBILITY] ?? null;
return $this->data[self::JSON_PROPERTY_VISIBILITY] ?? null;
}
/**
* @inheritDoc
*/
public function getColor(): ?string {
return $this->data[self::PROPERTY_COLOR] ?? null;
return $this->data[self::JSON_PROPERTY_COLOR] ?? null;
}
}
@@ -14,22 +14,15 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'chrono:collection';
public const PROPERTY_CONTENT = 'content';
public const JSON_PROPERTY_CONTENTS = 'content';
public const PROPERTY_LABEL = 'label';
public const PROPERTY_DESCRIPTION = 'description';
public const PROPERTY_RANK = 'rank';
public const PROPERTY_VISIBILITY = 'visibility';
public const PROPERTY_COLOR = 'color';
public const JSON_PROPERTY_LABEL = 'label';
public const JSON_PROPERTY_DESCRIPTION = 'description';
public const JSON_PROPERTY_PRIORITY = 'priority';
public const JSON_PROPERTY_VISIBILITY = 'visibility';
public const JSON_PROPERTY_COLOR = 'color';
/**
* Gets the content type of this collection
*
* @since 2025.05.01
*
* @return CollectionContent[] Returns an array of content types, a single content type, or null if not set
*/
public function content(): array;
public function content(): CollectionContent;
/**
* Gets the human friendly name of this collection (e.g. Personal Calendar)
@@ -46,11 +39,11 @@ interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface
public function getDescription(): ?string;
/**
* Gets the rank of this collection
* Gets the priority of this collection
*
* @since 2025.05.01
*/
public function getRank(): ?int;
public function getPriority(): ?int;
/**
* Gets the visibility of this collection
@@ -33,7 +33,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setLabel(string $value): static {
$this->data[self::PROPERTY_LABEL] = $value;
$this->data[self::JSON_PROPERTY_LABEL] = $value;
return $this;
}
@@ -41,15 +41,15 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setDescription(?string $value): static {
$this->data[self::PROPERTY_DESCRIPTION] = $value;
$this->data[self::JSON_PROPERTY_DESCRIPTION] = $value;
return $this;
}
/**
* @inheritDoc
*/
public function setRank(?int $value): static {
$this->data[self::PROPERTY_RANK] = $value;
public function setPriority(?int $value): static {
$this->data[self::JSON_PROPERTY_PRIORITY] = $value;
return $this;
}
@@ -57,7 +57,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setVisibility(?bool $value): static {
$this->data[self::PROPERTY_VISIBILITY] = $value;
$this->data[self::JSON_PROPERTY_VISIBILITY] = $value;
return $this;
}
@@ -65,7 +65,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setColor(?string $value): static {
$this->data[self::PROPERTY_COLOR] = $value;
$this->data[self::JSON_PROPERTY_COLOR] = $value;
return $this;
}
@@ -30,11 +30,11 @@ interface CollectionPropertiesMutableInterface extends CollectionPropertiesBaseI
public function setDescription(?string $value): static;
/**
* Sets the rank of this collection
* Sets the priority of this collection
*
* @since 2025.05.01
*/
public function setRank(?int $value): static;
public function setPriority(?int $value): static;
/**
* Sets the visibility of this collection
@@ -9,7 +9,6 @@ declare(strict_types=1);
namespace KTXF\Chrono\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeBaseAbstract;
/**
@@ -21,12 +20,7 @@ use KTXF\Resource\Provider\Node\NodeBaseAbstract;
*/
abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface {
protected string $type = 'chrono:entity';
protected EntityPropertiesBaseInterface $properties;
protected function nodeIdentifier(): EntityIdentifier {
return new EntityIdentifier($this->data[static::PROPERTY_PROVIDER], $this->data[static::PROPERTY_SERVICE], $this->data[static::PROPERTY_COLLECTION], $this->data[static::PROPERTY_IDENTIFIER]);
}
protected EntityPropertiesBaseAbstract $properties;
/**
* @inheritDoc
@@ -13,6 +13,8 @@ use KTXF\Resource\Provider\Node\NodeBaseInterface;
interface EntityBaseInterface extends NodeBaseInterface {
public const JSON_TYPE = 'chrono.entity';
/**
* Gets the entity properties
*
@@ -9,7 +9,6 @@ declare(strict_types=1);
namespace KTXF\Chrono\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeMutableAbstract;
use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
@@ -22,12 +21,9 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
*/
abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface {
protected string $type = 'chrono:entity';
protected EntityPropertiesMutableInterface $properties;
public const JSON_TYPE = EntityMutableInterface::JSON_TYPE;
protected function nodeIdentifier(): EntityIdentifier {
return new EntityIdentifier($this->data[static::PROPERTY_PROVIDER], $this->data[static::PROPERTY_SERVICE], (string) $this->data[static::PROPERTY_COLLECTION], (string) $this->data[static::PROPERTY_IDENTIFIER]);
}
protected EntityPropertiesMutableAbstract $properties;
/**
* @inheritDoc
@@ -44,7 +40,7 @@ abstract class EntityMutableAbstract extends NodeMutableAbstract implements Enti
throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface');
}
$this->properties = $value;
$this->properties->setDataRaw($value->getDataRaw());
return $this;
}
@@ -16,6 +16,8 @@ use KTXF\Resource\Provider\Node\NodeMutableInterface;
*/
interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface {
public const JSON_TYPE = EntityBaseInterface::JSON_TYPE;
/**
* Gets the entity properties (mutable)
*
@@ -13,10 +13,9 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface {
/**
* @inheritDoc
*/
public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE;
public function getDataRaw(): array|string|null {
return $this->data[self::PROPERTY_DATA] ?? null;
return $this->data[self::JSON_PROPERTY_DATA] ?? null;
}
}
@@ -14,7 +14,7 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'chrono:entity';
public const PROPERTY_DATA = 'data';
public const JSON_PROPERTY_DATA = 'data';
public function getDataRaw(): array|string|null;
@@ -24,7 +24,7 @@ abstract class EntityPropertiesMutableAbstract extends EntityPropertiesBaseAbstr
}
public function setDataRaw(array|string|null $value): static {
$this->data[self::PROPERTY_DATA] = $value;
$this->data[self::JSON_PROPERTY_DATA] = $value;
return $this;
}
}
@@ -13,6 +13,8 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
interface EntityPropertiesMutableInterface extends EntityPropertiesBaseInterface, NodePropertiesMutableInterface {
public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE;
public function setDataRaw(array|string|null $value): static;
}
@@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity;
use DateTimeInterface;
use KTXF\Chrono\Entity\Property\AttachmentCollection;
use KTXF\Chrono\Entity\Property\JournalStatusTypes;
use KTXF\Chrono\Entity\Property\JournalVisibilityTypes;
use KTXF\Chrono\Entity\Property\TagCollection;
use KTXF\Json\JsonSerializableObject;
class JournalObject extends JsonSerializableObject {
public string $type = 'journal';
public int $version = 1;
public ?string $urid = null;
public ?DateTimeInterface $created = null;
public ?DateTimeInterface $modified = null;
public ?string $label = null;
public ?string $description = null;
public TagCollection $tags;
public ?string $content = null;
public ?DateTimeInterface $startsOn = null;
public ?DateTimeInterface $endsOn = null;
public ?JournalStatusTypes $status = null;
public ?JournalVisibilityTypes $visibility = null;
public AttachmentCollection $attachments;
public function __construct() {
$this->tags = new TagCollection();
$this->attachments = new AttachmentCollection();
}
}
@@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection;
class AttachmentCollection extends JsonSerializableCollection {
public function __construct(array $data = []) {
parent::__construct($data, AttachmentObject::class, 'string');
}
}
@@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableObject;
class AttachmentObject extends JsonSerializableObject {
public ?string $uri = null;
public ?string $type = null;
public ?string $label = null;
public ?int $priority = null;
}
@@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
enum JournalStatusTypes: string {
case Draft = 'draft';
case Final = 'final';
case Cancelled = 'cancelled';
}
@@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
enum JournalVisibilityTypes: string {
case Public = 'public';
case Private = 'private';
case Confidential = 'confidential';
}
@@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection;
class TagCollection extends JsonSerializableCollection {
public function __construct(array $data = []) {
parent::__construct($data, 'string');
}
}
@@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
use DateTimeInterface;
use KTXF\Json\JsonSerializableObject;
class TaskRecurrenceObject extends JsonSerializableObject {
public ?string $frequency = null;
public ?int $interval = null;
public ?int $count = null;
public ?DateTimeInterface $until = null;
public array $byDay = [];
public array $byMonthDay = [];
public array $byMonth = [];
}
@@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
enum TaskStatusTypes: string {
case NeedsAction = 'needs-action';
case InProcess = 'in-process';
case Completed = 'completed';
case Cancelled = 'cancelled';
}
@@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection;
class TaskSubtaskCollection extends JsonSerializableCollection {
public function __construct(array $data = []) {
parent::__construct($data, TaskSubtaskObject::class, 'string');
}
}
@@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableObject;
class TaskSubtaskObject extends JsonSerializableObject {
public ?string $label = null;
public bool $completed = false;
public ?int $priority = null;
}
-41
View File
@@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity;
use DateTimeInterface;
use KTXF\Chrono\Entity\Property\AttachmentCollection;
use KTXF\Chrono\Entity\Property\TagCollection;
use KTXF\Chrono\Entity\Property\TaskRecurrenceObject;
use KTXF\Chrono\Entity\Property\TaskStatusTypes;
use KTXF\Chrono\Entity\Property\TaskSubtaskCollection;
use KTXF\Json\JsonSerializableObject;
class TaskObject extends JsonSerializableObject {
public string $type = 'task';
public int $version = 1;
public ?string $urid = null;
public ?DateTimeInterface $created = null;
public ?DateTimeInterface $modified = null;
public ?string $label = null;
public ?string $description = null;
public TagCollection $tags;
public ?DateTimeInterface $startsOn = null;
public ?DateTimeInterface $dueOn = null;
public ?DateTimeInterface $completedOn = null;
public ?TaskStatusTypes $status = null;
public ?int $priority = null;
public ?int $progress = null;
public ?string $color = null;
public ?TaskRecurrenceObject $recurrence = null;
public TaskSubtaskCollection $subtasks;
public AttachmentCollection $attachments;
public ?string $notes = null;
public function __construct() {
$this->tags = new TagCollection();
$this->subtasks = new TaskSubtaskCollection();
$this->attachments = new AttachmentCollection();
}
}
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventAvailabilityTypes: string {
case Free = 'free';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use DateInterval;
use DateTime;
@@ -33,7 +33,7 @@ class EventCommonObject extends JsonSerializableObject {
public EventSensitivityTypes|null $sensitivity = null;
public int|null $priority = null;
public string|null $color = null;
public TagCollection $tags;
public EventTagCollection $tags;
public EventOrganizerObject $organizer;
public EventParticipantCollection $participants;
public EventNotificationCollection $notifications;
@@ -44,7 +44,7 @@ class EventCommonObject extends JsonSerializableObject {
$this->locationsVirtual = new EventLocationVirtualCollection();
$this->notifications = new EventNotificationCollection();
$this->organizer = new EventOrganizerObject();
$this->tags = new TagCollection();
$this->tags = new EventTagCollection();
}
}
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use DateTime;
use DateTimeImmutable;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventNotificationAnchorTypes: string {
case Start = 'start';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use DateInterval;
use DateTime;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventNotificationPatterns: string {
case Absolute = 'absolute';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventNotificationTypes: string {
case Visual = 'visual';
@@ -7,12 +7,9 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity;
namespace KTXF\Chrono\Event;
use DateTimeInterface;
use KTXF\Chrono\Entity\Property\EventCommonObject;
use KTXF\Chrono\Entity\Property\EventMutationCollection;
use KTXF\Chrono\Entity\Property\EventOccurrenceObject;
class EventObject extends EventCommonObject {
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use DateTime;
use DateTimeImmutable;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventOccurrencePatternTypes: string {
case Absolute = 'absolute';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventOccurrencePrecisionTypes: string {
case Yearly = 'yearly';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventParticipantRealm: string {
case Internal = 'I';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventParticipantRoleTypes: string {
case Owner = 'owner';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventParticipantStatusTypes: string {
case None = 'none';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventParticipantTypes: string {
case Unknown = 'unknown';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Chrono\Entity\Property;
namespace KTXF\Chrono\Event;
enum EventSensitivityTypes: string {
case Public = 'public';
@@ -7,11 +7,11 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\People\Entity\Property;
namespace KTXF\Chrono\Event;
use KTXF\Json\JsonSerializableCollection;
class TagCollection extends JsonSerializableCollection {
class EventTagCollection extends JsonSerializableCollection {
public function __construct(array $data = []) {
parent::__construct($data, 'string');
@@ -12,7 +12,7 @@ namespace KTXF\Chrono\Provider;
use KTXF\Resource\Provider\ResourceProviderBaseInterface;
/**
* Provider Base Interface
* Chrono Provider Base Interface
*
* @since 2025.05.01
*/
@@ -12,9 +12,11 @@ namespace KTXF\Chrono\Provider;
use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
/**
* Provider Service Mutate Interface
* Chrono Provider Service Mutate Interface
*
* Optional interface for providers that support service CRUD operations
* Optional interface for providers that support service CRUD operations.
*
* Implementations return ServiceMutableInterface instances (which extend ResourceServiceMutateInterface).
*
* @since 2025.05.01
*
@@ -23,8 +25,11 @@ use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
* @method string serviceModify(string $tenantId, ?string $userId, ServiceMutableInterface $service) Modify a chrono service configuration
* @method bool serviceDestroy(string $tenantId, ?string $userId, ServiceMutableInterface $service) Delete a chrono service configuration
*/
interface ProviderServiceMutateInterface extends ResourceProviderServiceMutateInterface {
interface ProviderServiceMutateInterface extends ProviderBaseInterface, ResourceProviderServiceMutateInterface {
public const JSON_TYPE = ProviderBaseInterface::JSON_TYPE;
// Methods inherited from ResourceProviderServiceMutateInterface
// Implementations should return/accept ServiceMutableInterface instances
}
@@ -10,27 +10,36 @@ declare(strict_types=1);
namespace KTXF\Chrono\Provider;
use KTXF\Chrono\Service\ServiceBaseInterface;
use KTXF\Chrono\Service\ServiceMutableInterface;
/**
* Provider Service Test Interface
* Chrono Provider Service Test Interface
*
* Optional interface for providers that support testing service connections
* Optional interface for chrono providers that support testing service connections.
* Providers implementing this interface can validate connection parameters,
* test authentication, and verify service availability before creating a
* persistent service configuration.
*
* Supports two testing modes:
* 1. Testing an existing service (validate current configuration)
* 2. Testing a fresh configuration (validate before saving)
*
* @since 2025.05.01
*/
interface ProviderServiceTestInterface {
public const CAPABILITY_SERVICE_TEST = 'ServiceTest';
interface ProviderServiceTestInterface extends ProviderBaseInterface {
/**
* Test a service connection
*
* Tests connectivity, authentication, and capabilities of a service
* Tests connectivity, authentication, and capabilities of a service.
*
* For new services: use serviceFresh() to create a service, configure it with
* setters, then pass it to this method for testing before persisting.
*
* For existing services: fetch the service and pass it directly.
*
* @since 2025.05.01
*
* @param ServiceBaseInterface|ServiceMutableInterface $service Service to test (can be fresh/unsaved or existing)
* @param ServiceBaseInterface $service Service to test (can be fresh/unsaved or existing)
* @param array $options Provider-specific test options:
* - 'timeout' => int (seconds, default: 10)
* - 'verify_ssl' => bool (default: true)
@@ -39,10 +48,10 @@ interface ProviderServiceTestInterface {
*
* @return array Test results in the format:
* [
* 'disposition' => 'Success' | 'Failure' | 'Warning',
* 'message' => 'Connection successful' | 'Error message',
* 'success' => bool,
* 'message' => 'Connection successful' | 'Error message'
* ]
*/
public function serviceTest(ServiceBaseInterface|ServiceMutableInterface $service, array $options = []): array;
public function serviceTest(ServiceBaseInterface $service, array $options = []): array;
}
@@ -9,21 +9,16 @@ declare(strict_types=1);
namespace KTXF\Chrono\Service;
use Generator;
use KTXF\Chrono\Collection\CollectionBaseInterface;
use KTXF\Chrono\Entity\EntityBaseInterface;
use KTXF\Resource\Delta\Delta;
use KTXF\Resource\Filter\IFilter;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
use KTXF\Resource\Provider\ResourceServiceBaseInterface;
use KTXF\Resource\Range\IRange;
use KTXF\Resource\Range\RangeType;
use KTXF\Resource\Sort\ISort;
/**
* Service Base Interface
*
* Minimum interface for a service, providing read-only access to collections and entities.
* Chrono Service Base Interface
*
* @since 2025.05.01
*/
@@ -75,7 +70,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order
*
* @return array<string|int,CollectionBaseInterface> Collections indexed by identifier
* @return array<string|int,CollectionBaseInterface> Collections indexed by ID
*/
public function collectionList(string|int $location, ?IFilter $filter = null, ?ISort $sort = null): array;
@@ -102,9 +97,9 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
*
* @since 2025.05.01
*
* @param string|int ...$identifiers Collection identifiers to check
* @param string|int ...$identifiers Collection IDs to check
*
* @return array<string|int,bool> Map of identifier => exists
* @return array<string|int,bool> Map of ID => exists
*/
public function collectionExtant(string|int $location, string|int ...$identifiers): array;
@@ -113,44 +108,29 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
*
* @since 2025.05.01
*
* @param string|int $identifier Collection identifier
* @param string|int $identifier Collection ID
*
* @return CollectionBaseInterface|null Collection or null if not found
*/
public function collectionFetch(string|int $identifier): ?CollectionBaseInterface;
/**
* Lists entities in a collection
* Lists messages in a collection
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string|int $collection Collection ID
* @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination
* @param array|null $properties Optional entity properties to fetch
* @param array|null $properties Optional message properties to fetch
*
* @return array<string,EntityBaseInterface> Entities indexed by Urn
* @return array<string|int,EntityBaseInterface> Messages indexed by ID
*/
public function entityListBulk(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array;
public function entityList(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array;
/**
* Lists entities in a collection
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination
* @param array|null $properties Optional entity properties to fetch
*
* @return Generator Yields entities one by one as EntityBaseInterface
*/
public function entityListStream(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): Generator;
/**
* Creates a filter builder for entities
* Creates a filter builder for messages
*
* @since 2025.05.01
*
@@ -159,7 +139,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListFilter(): IFilter;
/**
* Creates a sort builder for entities
* Creates a sort builder for messages
*
* @since 2025.05.01
*
@@ -168,7 +148,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListSort(): ISort;
/**
* Creates a range builder for entities
* Creates a range builder for messages
*
* @since 2025.05.01
*
@@ -179,49 +159,40 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListRange(RangeType $type): IRange;
/**
* Gets incremental changes since last signature
* Gets incremental changes since last sync
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string $signature Token from previous delta
* @param string|int $collection Collection ID
* @param string $signature Sync token from previous sync
* @param string $detail Detail level: 'ids', 'minimal', 'full'
*
* @return array ['signature' => string, 'added' => array, 'modified' => array, 'removed' => array]
*/
public function entityDelta(string|int $collection, string $signature): Delta;
public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta;
/**
* Checks if entities exist
* Checks if messages exist
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string|int ...$identifiers Entity identifiers to check
* @param string|int $collection Collection ID
* @param string|int ...$identifiers Message IDs to check
*
* @return array<string|int,bool> Map of identifier => exists
* @return array<string|int,bool> Map of ID => exists
*/
public function entityExtant(string|int $collection, string|int ...$identifiers): array;
/**
* Fetches one or more entities as an array
* Fetches one or more entities
*
* @since 2025.05.01
*
* @param EntityIdentifierInterface ...$identifiers Entity identifiers to fetch
* @param string|int $collection Collection ID
* @param string|int ...$identifiers Message IDs to fetch
*
* @return array<string,EntityBaseInterface> Entities indexed by Urn
* @return array<string|int,EntityBaseInterface> Messages indexed by ID
*/
public function entityFetchBulk(EntityIdentifierInterface ...$identifiers): array;
/**
* Fetches one or more entities as a stream
*
* @since 2025.05.01
*
* @param EntityIdentifierInterface ...$identifiers Entity identifiers to fetch
*
* @return Generator<string,EntityBaseInterface> Yields entities one by one
*/
public function entityFetchStream(EntityIdentifierInterface ...$identifiers): Generator;
public function entityFetch(string|int $collection, string|int ...$identifiers): array;
}
@@ -11,17 +11,13 @@ namespace KTXF\Chrono\Service;
use KTXF\Chrono\Collection\CollectionBaseInterface;
use KTXF\Chrono\Collection\CollectionMutableInterface;
use KTXF\Chrono\Collection\CollectionPropertiesBaseInterface;
use KTXF\Resource\Identifier\CollectionIdentifierInterface;
/**
* Service Collection Mutable Interface
*
* Optional interface for services that support collection CRUD operations.
* Chrono Service Collection Mutable Interface
*
* @since 2025.05.01
*/
interface ServiceCollectionMutableInterface {
interface ServiceCollectionMutableInterface extends ServiceBaseInterface {
public const CAPABILITY_COLLECTION_CREATE = 'CollectionCreate';
public const CAPABILITY_COLLECTION_UPDATE = 'CollectionUpdate';
@@ -41,36 +37,37 @@ interface ServiceCollectionMutableInterface {
*
* @since 2025.05.01
*
* @param CollectionIdentifierInterface|null $target Target collection identifier (parent)
* @param CollectionPropertiesBaseInterface $properties Collection properties
* @param string|int|null $location Parent collection ID (null for root)
* @param CollectionMutableInterface $collection Collection to create
* @param array $options Protocol-specific options
*
* @return CollectionBaseInterface Created collection with assigned ID
*/
public function collectionCreate(CollectionIdentifierInterface|null $target, CollectionPropertiesBaseInterface $properties, array $options = []): CollectionBaseInterface;
public function collectionCreate(string|int|null $location, CollectionMutableInterface $collection, array $options = []): CollectionBaseInterface;
/**
* Updates an existing collection
*
* @since 2025.05.01
*
* @param CollectionIdentifierInterface $target Target collection identifier
* @param CollectionPropertiesBaseInterface $properties Updated collection data
* @param string|int $identifier Collection ID
* @param CollectionMutableInterface $collection Updated collection data
*
* @return CollectionBaseInterface Updated collection
*/
public function collectionUpdate(CollectionIdentifierInterface $target, CollectionPropertiesBaseInterface $properties): CollectionBaseInterface;
public function collectionUpdate(string|int $identifier, CollectionMutableInterface $collection): CollectionBaseInterface;
/**
* Deletes a collection
*
* @since 2025.05.01
*
* @param CollectionIdentifierInterface $target Target collection identifier
* @param string|int $identifier Collection ID
* @param bool $force Force deletion even if not empty
* @param bool $recursive Recursively delete contents
*
* @return CollectionBaseInterface|true Collection object on soft delete, true on hard delete
* @return bool True if deleted
*/
public function collectionDelete(CollectionIdentifierInterface $target, bool $force = false): CollectionBaseInterface | true;
public function collectionDelete(string|int $identifier, bool $force = false, bool $recursive = false): bool;
}
@@ -12,12 +12,15 @@ namespace KTXF\Chrono\Service;
use KTXF\Resource\Provider\ResourceServiceConfigureInterface;
/**
* Service Configurable Interface
* Chrono Service Configurable Interface
*
* Extends base service interface with setter methods for mutable properties
* Extends base service interface with setter methods for mutable properties.
* Used for service configuration and updates.
*
* @since 2025.05.01
*/
interface ServiceConfigurableInterface extends ServiceMutableInterface, ResourceServiceConfigureInterface {
public const JSON_TYPE = ServiceBaseInterface::JSON_TYPE;
}
@@ -11,21 +11,19 @@ namespace KTXF\Chrono\Service;
use KTXF\Chrono\Entity\EntityBaseInterface;
use KTXF\Chrono\Entity\EntityMutableInterface;
use KTXF\Chrono\Entity\EntityPropertiesMutableInterface;
use KTXF\Resource\Identifier\CollectionIdentifierInterface;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
/**
* Service Entity Mutable Interface
* Chrono Service Entity Mutable Interface
*
* Optional interface for services that support entity CRUD operations
* Optional interface for services that support entity CRUD operations.
* Provides entity creation, modification, deletion, copying, moving, and flag management.
*
* @since 2025.05.01
*/
interface ServiceEntityMutableInterface {
interface ServiceEntityMutableInterface extends ServiceBaseInterface {
public const CAPABILITY_ENTITY_CREATE = 'EntityCreate';
public const CAPABILITY_ENTITY_MODIFY = 'EntityModify';
public const CAPABILITY_ENTITY_UPDATE = 'EntityUpdate';
public const CAPABILITY_ENTITY_DELETE = 'EntityDelete';
public const CAPABILITY_ENTITY_COPY = 'EntityCopy';
public const CAPABILITY_ENTITY_MOVE = 'EntityMove';
@@ -44,71 +42,36 @@ interface ServiceEntityMutableInterface {
*
* @since 2025.05.01
*
* @param CollectionIdentifierInterface $target Target collection identifier
* @param EntityPropertiesMutableInterface $properties Entity properties
* @param array $options Additional options
* @param string|int $collection collection identifier
* @param EntityMutableInterface $entity Entity data
* @param array $options additional options
*
* @return EntityBaseInterface Created entity
*/
public function entityCreate(CollectionIdentifierInterface $target, EntityPropertiesMutableInterface $properties, array $options = []): EntityBaseInterface;
public function entityCreate(string|int $collection, EntityMutableInterface $entity, array $options = []): EntityBaseInterface;
/**
* Modifies an existing entity
*
* @since 2025.05.01
*
* @param EntityIdentifierInterface $target Target entity identifier
* @param EntityPropertiesMutableInterface $properties Entity properties to update
* @param string|int $collection Collection identifier
* @param string|int $identifier Entity identifier
* @param EntityMutableInterface $entity Entity data
*
* @return EntityBaseInterface Modified entity
*/
public function entityModify(EntityIdentifierInterface $target, EntityPropertiesMutableInterface $properties): EntityBaseInterface;
public function entityUpdate(string|int $collection, string|int $identifier, EntityMutableInterface $entity): EntityBaseInterface;
/**
* Deletes entities
*
* @since 2026.04.01
*
* @param EntityIdentifierInterface ...$targets Source entities to delete
*
* @return array<string, array{
* disposition: 'moved'|'deleted'|'error',
* destination: ?CollectionIdentifierInterface,
* mutation: EntityIdentifierInterface
* }> Results keyed by source entity identifier
*/
public function entityDelete(EntityIdentifierInterface ...$targets): array;
/**
* Moves entities to another collection
* Deletes an existing entity in the specified collection
*
* @since 2025.05.01
*
* @param CollectionIdentifierInterface $target Target collection identifier
* @param EntityIdentifierInterface ...$sources Source entities to move
* @param string|int $collection Collection identifier
* @param string|int $identifier Entity identifier to delete
*
* @return array<string, array{
* disposition: 'moved'|'error',
* destination: ?CollectionIdentifierInterface,
* mutation: EntityIdentifierInterface
* }> Results keyed by source entity identifier
* @return EntityBaseInterface Deleted entity
*/
public function entityMove(CollectionIdentifierInterface $target, EntityIdentifierInterface ...$sources): array;
/**
* Copies entities to another collection
*
* @since 2025.05.01
*
* @param CollectionIdentifierInterface $target Target collection identifier
* @param EntityIdentifierInterface ...$sources Source entities to copy
*
* @return array<string, array{
* disposition: 'copied'|'error',
* destination: ?CollectionIdentifierInterface,
* mutation: EntityIdentifierInterface
* }> Results keyed by source entity identifier
*/
public function entityCopy(CollectionIdentifierInterface $target, EntityIdentifierInterface ...$sources): array;
public function entityDelete(string|int $collection, string|int $identifier): EntityBaseInterface;
}
@@ -12,10 +12,11 @@ namespace KTXF\Chrono\Service;
/**
* Chrono Service Mutable Interface
*
* Extends base service interface with setter methods for mutable properties
* Extends base service interface with setter methods for mutable properties.
* Used for service configuration and updates.
*
* @since 2025.05.01
*/
interface ServiceMutableInterface {
interface ServiceMutableInterface extends ServiceBaseInterface {
}
+10 -10
View File
@@ -14,16 +14,16 @@ use DateTimeImmutable;
interface INodeBase extends \JsonSerializable {
public const JSON_TYPE = 'files.node';
public const PROPERTY_TYPE = '@type';
public const PROPERTY_IN = 'in';
public const PROPERTY_ID = 'id';
public const PROPERTY_CREATED_ON = 'createdOn';
public const PROPERTY_CREATED_BY = 'createdBy';
public const PROPERTY_MODIFIED_ON = 'modifiedOn';
public const PROPERTY_MODIFIED_BY = 'modifiedBy';
public const PROPERTY_OWNER = 'owner';
public const PROPERTY_SIGNATURE = 'signature';
public const PROPERTY_LABEL = 'label';
public const JSON_PROPERTY_TYPE = '@type';
public const JSON_PROPERTY_IN = 'in';
public const JSON_PROPERTY_ID = 'id';
public const JSON_PROPERTY_CREATED_ON = 'createdOn';
public const JSON_PROPERTY_CREATED_BY = 'createdBy';
public const JSON_PROPERTY_MODIFIED_ON = 'modifiedOn';
public const JSON_PROPERTY_MODIFIED_BY = 'modifiedBy';
public const JSON_PROPERTY_OWNER = 'owner';
public const JSON_PROPERTY_SIGNATURE = 'signature';
public const JSON_PROPERTY_LABEL = 'label';
/**
* Unique identifier of the parent node (folder) this node belongs to
+4 -4
View File
@@ -18,10 +18,10 @@ namespace KTXF\Files\Node;
interface INodeEntityBase extends INodeBase {
public const JSON_TYPE = 'files.entity';
public const PROPERTY_SIZE = 'size';
public const PROPERTY_MIME = 'mime';
public const PROPERTY_FORMAT = 'format';
public const PROPERTY_ENCODING = 'encoding';
public const JSON_PROPERTY_SIZE = 'size';
public const JSON_PROPERTY_MIME = 'mime';
public const JSON_PROPERTY_FORMAT = 'format';
public const JSON_PROPERTY_ENCODING = 'encoding';
/**
* File size in bytes
@@ -14,15 +14,15 @@ use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeBaseAbstract;
/**
* Abstract Entity Base Class
* Abstract Mail Entity Base Class
*
* Provides common implementation for entities
* Provides common implementation for mail entities
*
* @since 2025.05.01
*/
abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface {
protected string $type = 'mail:entity';
protected string $type = 'mail.entity';
protected MessagePropertiesBaseInterface $properties;
protected function nodeIdentifier(): EntityIdentifier {
@@ -15,15 +15,15 @@ use KTXF\Resource\Provider\Node\NodeMutableAbstract;
use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
/**
* Abstract Entity Mutable Class
* Abstract Mail Entity Mutable Class
*
* Provides common implementation for mutable entities
* Provides common implementation for mutable mail entities
*
* @since 2025.05.01
*/
abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface {
protected string $type = 'mail:entity';
protected string $type = 'mail.entity';
protected MessagePropertiesMutableInterface $properties;
protected function nodeIdentifier(): EntityIdentifier {
+13 -20
View File
@@ -25,6 +25,16 @@ class Address implements AddressInterface {
private ?string $name = null,
) {}
/**
* @inheritDoc
*/
public function jsonSerialize(): array {
return array_filter([
self::JSON_PROPERTY_ADDRESS => $this->address,
self::JSON_PROPERTY_LABEL => $this->name,
], fn($v) => $v !== null && $v !== '');
}
/**
* Creates an Address from a formatted string
*
@@ -56,34 +66,17 @@ class Address implements AddressInterface {
*
* @since 2025.05.01
*
* @param array<address: string, label: string|null> $data
* @param array $data Array with 'address' and optional 'name' keys
*
* @return self
*/
public static function fromArray(array $data): self {
return new self(
$data[self::PROPERTY_ADDRESS] ?? $data['address'] ?? '',
$data[self::PROPERTY_LABEL] ?? $data['label'] ?? null,
$data[self::JSON_PROPERTY_ADDRESS] ?? $data['address'] ?? '',
$data[self::JSON_PROPERTY_LABEL] ?? $data['name'] ?? null,
);
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array {
return array_filter([
self::PROPERTY_ADDRESS => $this->address,
self::PROPERTY_LABEL => $this->name,
], fn($v) => $v !== null && $v !== '');
}
/**
* @inheritDoc
*/
public function toArray(): array {
return $this->jsonSerialize();
}
/**
* @inheritDoc
*/
+2 -9
View File
@@ -20,15 +20,8 @@ use KTXF\Json\JsonSerializable;
*/
interface AddressInterface extends JsonSerializable {
public const PROPERTY_ADDRESS = 'address';
public const PROPERTY_LABEL = 'label';
/*
* Converts the Address to an array representation
*
* @return array<address: string, label: string|null>
*/
public function toArray(): array;
public const JSON_PROPERTY_ADDRESS = 'address';
public const JSON_PROPERTY_LABEL = 'label';
/**
* Gets the email address
+194
View File
@@ -0,0 +1,194 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Mail\Object;
/**
* Attachment Implementation
*
* @since 2025.05.01
*/
class Attachment implements AttachmentInterface {
/**
* @param string $name File name
* @param string $mimeType MIME type
* @param string $content Binary content
* @param string|null $id Attachment ID
* @param int|null $size Size in bytes
* @param string|null $contentId Content-ID for inline attachments
* @param bool $inline Whether inline attachment
*/
public function __construct(
private string $name,
private string $mimeType,
private string $content,
private ?string $id = null,
private ?int $size = null,
private ?string $contentId = null,
private bool $inline = false,
) {
if ($this->size === null) {
$this->size = strlen($this->content);
}
}
/**
* @inheritDoc
*/
public function jsonSerialize(): array {
return array_filter([
self::JSON_PROPERTY_ID => $this->id,
self::JSON_PROPERTY_NAME => $this->name,
self::JSON_PROPERTY_MIME_TYPE => $this->mimeType,
self::JSON_PROPERTY_SIZE => $this->size,
self::JSON_PROPERTY_CONTENT_ID => $this->contentId,
self::JSON_PROPERTY_INLINE => $this->inline ?: null,
'contentBase64' => $this->getContentBase64(),
], fn($v) => $v !== null);
}
/**
* Creates an attachment from a file path
*
* @since 2025.05.01
*
* @param string $path File path
* @param string|null $name Override file name
* @param string|null $mimeType Override MIME type
*
* @return self
*/
public static function fromFile(string $path, ?string $name = null, ?string $mimeType = null): self {
$content = file_get_contents($path);
$name = $name ?? basename($path);
$mimeType = $mimeType ?? mime_content_type($path) ?: 'application/octet-stream';
return new self($name, $mimeType, $content);
}
/**
* Creates an attachment from base64 encoded content
*
* @since 2025.05.01
*
* @param string $name File name
* @param string $mimeType MIME type
* @param string $base64Content Base64 encoded content
*
* @return self
*/
public static function fromBase64(string $name, string $mimeType, string $base64Content): self {
return new self($name, $mimeType, base64_decode($base64Content));
}
/**
* Creates an inline attachment for embedding in HTML
*
* @since 2025.05.01
*
* @param string $name File name
* @param string $mimeType MIME type
* @param string $content Binary content
* @param string $contentId Content-ID (without cid: prefix)
*
* @return self
*/
public static function inline(string $name, string $mimeType, string $content, string $contentId): self {
return new self($name, $mimeType, $content, null, null, $contentId, true);
}
/**
* Creates from array data
*
* @since 2025.05.01
*
* @param array $data
*
* @return self
*/
public static function fromArray(array $data): self {
$content = $data['content'] ?? '';
if (isset($data['contentBase64'])) {
$content = base64_decode($data['contentBase64']);
}
return new self(
$data[self::JSON_PROPERTY_NAME] ?? $data['name'] ?? '',
$data[self::JSON_PROPERTY_MIME_TYPE] ?? $data['mimeType'] ?? 'application/octet-stream',
$content,
$data[self::JSON_PROPERTY_ID] ?? $data['id'] ?? null,
$data[self::JSON_PROPERTY_SIZE] ?? $data['size'] ?? null,
$data[self::JSON_PROPERTY_CONTENT_ID] ?? $data['contentId'] ?? null,
$data[self::JSON_PROPERTY_INLINE] ?? $data['inline'] ?? false,
);
}
/**
* @inheritDoc
*/
public function getId(): ?string {
return $this->id;
}
/**
* @inheritDoc
*/
public function getName(): string {
return $this->name;
}
/**
* @inheritDoc
*/
public function getMimeType(): string {
return $this->mimeType;
}
/**
* @inheritDoc
*/
public function getSize(): ?int {
return $this->size;
}
/**
* @inheritDoc
*/
public function getContentId(): ?string {
return $this->contentId;
}
/**
* @inheritDoc
*/
public function isInline(): bool {
return $this->inline;
}
/**
* @inheritDoc
*/
public function getContent(): string {
return $this->content;
}
/**
* Gets the content as base64 encoded string
*
* @since 2025.05.01
*
* @return string
*/
public function getContentBase64(): string {
return base64_encode($this->content);
}
}
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Mail\Object;
use KTXF\Json\JsonSerializable;
/**
* Attachment Interface
*
* Represents a file attachment on a mail message.
*
* @since 2025.05.01
*/
interface AttachmentInterface extends JsonSerializable {
public const JSON_PROPERTY_ID = 'id';
public const JSON_PROPERTY_NAME = 'name';
public const JSON_PROPERTY_MIME_TYPE = 'mimeType';
public const JSON_PROPERTY_SIZE = 'size';
public const JSON_PROPERTY_CONTENT_ID = 'contentId';
public const JSON_PROPERTY_INLINE = 'inline';
/**
* Gets the attachment identifier
*
* @since 2025.05.01
*
* @return string|null Attachment ID or null for new attachments
*/
public function getId(): ?string;
/**
* Gets the file name
*
* @since 2025.05.01
*
* @return string File name (e.g., "document.pdf")
*/
public function getName(): string;
/**
* Gets the MIME type
*
* @since 2025.05.01
*
* @return string MIME type (e.g., "application/pdf")
*/
public function getMimeType(): string;
/**
* Gets the file size in bytes
*
* @since 2025.05.01
*
* @return int|null Size in bytes or null if unknown
*/
public function getSize(): ?int;
/**
* Gets the Content-ID for inline attachments
*
* @since 2025.05.01
*
* @return string|null Content-ID for referencing in HTML body (e.g., "cid:image1")
*/
public function getContentId(): ?string;
/**
* Checks if this is an inline attachment (embedded in body)
*
* @since 2025.05.01
*
* @return bool True if inline, false if regular attachment
*/
public function isInline(): bool;
/**
* Gets the attachment content
*
* @since 2025.05.01
*
* @return string Binary content of the attachment
*/
public function getContent(): string;
}
-37
View File
@@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Mail\Object;
/**
* Shared Message Part implementation matching the frontend JSON shape.
*/
class MessagePart extends MessagePartMutableAbstract {
public static function fromArray(array $data): self {
$part = new self();
$part->data = $data;
if (isset($part->data[MessagePartInterface::PROPERTY_SUB_PARTS]) && is_array($part->data[MessagePartInterface::PROPERTY_SUB_PARTS])) {
foreach ($part->data[MessagePartInterface::PROPERTY_SUB_PARTS] as $entry) {
if (is_object($entry)) {
$entry = get_object_vars($entry);
}
if (is_array($entry)) {
$part->parts[] = self::fromArray($entry);
}
}
unset($part->data[MessagePartInterface::PROPERTY_SUB_PARTS]);
}
return $part;
}
}
@@ -32,9 +32,9 @@ abstract class MessagePartBaseAbstract implements MessagePartInterface {
/**
* Constructor
*
* @param array|null &$data Reference to data array
* @param array &$data Reference to data array
*/
public function __construct(array|null &$data = null) {
public function __construct(array &$data = null) {
if ($data === null) {
$data = [];
}
@@ -52,70 +52,49 @@ abstract class MessagePartBaseAbstract implements MessagePartInterface {
* @inheritDoc
*/
public function getId(): ?string {
return $this->data[MessagePartInterface::PROPERTY_PART_ID] ?? null;
}
/**
* @inheritDoc
*/
public function getSize(): ?int {
return $this->data[MessagePartInterface::PROPERTY_SIZE] ?? null;
return $this->data['partId'] ?? null;
}
/**
* @inheritDoc
*/
public function getType(): ?string {
return $this->data[MessagePartInterface::PROPERTY_TYPE] ?? null;
return $this->data['type'] ?? null;
}
/**
* @inheritDoc
*/
public function getDisposition(): ?string {
return $this->data[MessagePartInterface::PROPERTY_DISPOSITION] ?? null;
return $this->data['disposition'] ?? null;
}
/**
* @inheritDoc
*/
public function getName(): ?string {
return $this->data[MessagePartInterface::PROPERTY_NAME] ?? null;
return $this->data['name'] ?? null;
}
/**
* @inheritDoc
*/
public function getCharset(): ?string {
return $this->data[MessagePartInterface::PROPERTY_CHARSET] ?? null;
}
/**
* @inheritDoc
*/
public function getContentId(): ?string {
return $this->data[MessagePartInterface::PROPERTY_CONTENT_ID] ?? null;
return $this->data['charset'] ?? null;
}
/**
* @inheritDoc
*/
public function getLanguage(): ?string {
return $this->data[MessagePartInterface::PROPERTY_LANGUAGE] ?? null;
return $this->data['language'] ?? null;
}
/**
* @inheritDoc
*/
public function getLocation(): ?string {
return $this->data[MessagePartInterface::PROPERTY_LOCATION] ?? null;
}
/**
* @inheritDoc
*/
public function getContent(): ?string {
return $this->data[MessagePartInterface::PROPERTY_CONTENT] ?? null;
return $this->data['location'] ?? null;
}
/**
@@ -132,9 +111,9 @@ abstract class MessagePartBaseAbstract implements MessagePartInterface {
$result = $this->data;
if (!empty($this->parts)) {
$result[MessagePartInterface::PROPERTY_SUB_PARTS] = [];
$result['subParts'] = [];
foreach ($this->parts as $part) {
$result[MessagePartInterface::PROPERTY_SUB_PARTS][] = $part->jsonSerialize();
$result['subParts'][] = $part->jsonSerialize();
}
}
@@ -20,19 +20,6 @@ use KTXF\Json\JsonSerializable;
*/
interface MessagePartInterface extends JsonSerializable {
public const PROPERTY_PART_ID = 'partId';
public const PROPERTY_BLOB_ID = 'blobId';
public const PROPERTY_SIZE = 'size';
public const PROPERTY_NAME = 'name';
public const PROPERTY_TYPE = 'type';
public const PROPERTY_CHARSET = 'charset';
public const PROPERTY_DISPOSITION = 'disposition';
public const PROPERTY_CONTENT_ID = 'cid';
public const PROPERTY_LANGUAGE = 'language';
public const PROPERTY_LOCATION = 'location';
public const PROPERTY_CONTENT = 'content';
public const PROPERTY_SUB_PARTS = 'subParts';
/**
* Gets the blob identifier
*
@@ -51,15 +38,6 @@ interface MessagePartInterface extends JsonSerializable {
*/
public function getId(): ?string;
/**
* Gets the part size in bytes
*
* @since 2026.06.15
*
* @return int|null
*/
public function getSize(): ?int;
/**
* Gets the MIME type
*
@@ -78,15 +56,6 @@ interface MessagePartInterface extends JsonSerializable {
*/
public function getDisposition(): ?string;
/**
* Gets the Content-ID for inline parts
*
* @since 2026.06.15
*
* @return string|null
*/
public function getContentId(): ?string;
/**
* Gets the part name
*
@@ -123,15 +92,6 @@ interface MessagePartInterface extends JsonSerializable {
*/
public function getLocation(): ?string;
/**
* Gets the decoded part content
*
* @since 2026.06.15
*
* @return string|null
*/
public function getContent(): ?string;
/**
* Gets the sub-parts
*
@@ -46,16 +46,6 @@ abstract class MessagePartMutableAbstract extends MessagePartBaseAbstract {
return $this;
}
/**
* Sets the part size
*
* @since 2026.06.15
*/
public function setSize(?int $value): static {
$this->data['size'] = $value;
return $this;
}
/**
* Sets the MIME type
*
@@ -112,16 +102,6 @@ abstract class MessagePartMutableAbstract extends MessagePartBaseAbstract {
return $this;
}
/**
* Sets the content id
*
* @since 2026.06.15
*/
public function setContentId(?string $value): static {
$this->data['cid'] = $value;
return $this;
}
/**
* Sets the language
*
@@ -150,16 +130,6 @@ abstract class MessagePartMutableAbstract extends MessagePartBaseAbstract {
return $this;
}
/**
* Sets the content payload
*
* @since 2026.06.15
*/
public function setContent(?string $value): static {
$this->data['content'] = $value;
return $this;
}
/**
* Sets the sub-parts
*
@@ -10,7 +10,6 @@ declare(strict_types=1);
namespace KTXF\Mail\Object;
use DateTimeImmutable;
use Exception;
use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
/**
@@ -22,110 +21,7 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
*/
abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract implements MessagePropertiesBaseInterface {
protected string $type = 'mail:message';
protected function extractBodyContent(?MessagePartInterface $part, string $type): ?string {
if ($part === null) {
return null;
}
if ($part->getType() === $type) {
$content = $part->getContent();
return is_string($content) && $content !== '' ? $content : null;
}
foreach ($part->getParts() as $subPart) {
$content = $this->extractBodyContent($subPart, $type);
if ($content !== null) {
return $content;
}
}
return null;
}
protected function materializeAddress(mixed $value): ?AddressInterface {
if ($value instanceof AddressInterface) {
return $value;
}
if (is_string($value) && $value !== '') {
return Address::fromString($value);
}
if (!is_array($value) || $value === []) {
return null;
}
if (array_is_list($value)) {
return $this->materializeAddress($value[0] ?? null);
}
return Address::fromArray([
AddressInterface::PROPERTY_ADDRESS => $value[AddressInterface::PROPERTY_ADDRESS],
AddressInterface::PROPERTY_LABEL => $value[AddressInterface::PROPERTY_LABEL] ?? null,
]);
}
/**
* @return array<int,AddressInterface>
*/
protected function materializeAddresses(array $values): array {
$addresses = [];
foreach ($values as $value) {
$address = $this->materializeAddress($value);
if ($address !== null) {
$addresses[] = $address;
}
}
return $addresses;
}
protected function materializeDate(mixed $value): ?DateTimeImmutable {
if ($value instanceof DateTimeImmutable) {
return $value;
}
if (!is_string($value) || $value === '') {
return null;
}
try {
return new DateTimeImmutable($value);
} catch (Exception) {
return null;
}
}
protected function materializePart(mixed $value): ?MessagePartInterface {
if ($value instanceof MessagePartInterface) {
return $value;
}
if (!is_array($value) || $value === []) {
return null;
}
return MessagePart::fromArray($value);
}
/**
* @return array<int,MessagePartInterface>
*/
protected function materializeParts(array $values): array {
$parts = [];
foreach ($values as $value) {
$part = $this->materializePart($value);
if ($part !== null) {
$parts[] = $part;
}
}
return $parts;
}
protected string $type = 'mail.message';
/**
* @inheritDoc
@@ -180,56 +76,56 @@ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract
* @inheritDoc
*/
public function getReceived(): ?DateTimeImmutable {
return $this->materializeDate($this->data[static::PROPERTY_RECEIVED] ?? null);
return $this->data[static::PROPERTY_RECEIVED] ?? null;
}
/**
* @inheritDoc
*/
public function getSent(): ?DateTimeImmutable {
return $this->materializeDate($this->data[static::PROPERTY_SENT] ?? null);
return $this->data[static::PROPERTY_SENT] ?? null;
}
/**
* @inheritDoc
*/
public function getSender(): ?AddressInterface {
return $this->materializeAddress($this->data[static::PROPERTY_SENDER] ?? null);
return $this->data[static::PROPERTY_SENDER] ?? null;
}
/**
* @inheritDoc
*/
public function getFrom(): ?AddressInterface {
return $this->materializeAddress($this->data[static::PROPERTY_FROM] ?? null);
return $this->data[static::PROPERTY_FROM] ?? null;
}
/**
* @inheritDoc
*/
public function getReplyTo(): array {
return $this->materializeAddresses($this->data[static::PROPERTY_REPLY_TO] ?? []);
return $this->data[static::PROPERTY_REPLY_TO] ?? [];
}
/**
* @inheritDoc
*/
public function getTo(): array {
return $this->materializeAddresses($this->data[static::PROPERTY_TO] ?? []);
return $this->data[static::PROPERTY_TO] ?? [];
}
/**
* @inheritDoc
*/
public function getCc(): array {
return $this->materializeAddresses($this->data[static::PROPERTY_CC] ?? []);
return $this->data[static::PROPERTY_CC] ?? [];
}
/**
* @inheritDoc
*/
public function getBcc(): array {
return $this->materializeAddresses($this->data[static::PROPERTY_BCC] ?? []);
return $this->data[static::PROPERTY_BCC] ?? [];
}
/**
@@ -243,38 +139,36 @@ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract
* @inheritDoc
*/
public function getBody(): ?MessagePartInterface {
return $this->materializePart($this->data[static::PROPERTY_BODY] ?? null);
return $this->data[static::PROPERTY_BODY] ?? null;
}
/**
* @inheritDoc
*/
public function hasBody(): bool {
$body = $this->getBody();
return $this->extractBodyContent($body, 'text/plain') !== null
|| $this->extractBodyContent($body, 'text/html') !== null;
return ($this->data[static::PROPERTY_BODY_TEXT_PLAIN] !== null && $this->data[static::PROPERTY_BODY_TEXT_PLAIN] !== '')
|| ($this->data[static::PROPERTY_BODY_TEXT_HTML] !== null && $this->data[static::PROPERTY_BODY_TEXT_HTML] !== '');
}
/**
* @inheritDoc
*/
public function getBodyTextPlain(): ?string {
return $this->extractBodyContent($this->getBody(), 'text/plain');
return $this->data[static::PROPERTY_BODY_TEXT_PLAIN] ?? null;
}
/**
* @inheritDoc
*/
public function getBodyTextHtml(): ?string {
return $this->extractBodyContent($this->getBody(), 'text/html');
return $this->data[static::PROPERTY_BODY_TEXT_HTML] ?? null;
}
/**
* @inheritDoc
*/
public function getAttachments(): array {
return $this->materializeParts($this->data[static::PROPERTY_ATTACHMENTS] ?? []);
return $this->data[static::PROPERTY_ATTACHMENTS] ?? [];
}
/**
@@ -282,7 +176,7 @@ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract
*/
public function getFlags(): array {
return $this->data[static::PROPERTY_FLAGS] ?? [
'seen' => false,
'read' => false,
'starred' => false,
'important' => false,
'answered' => false,
@@ -34,6 +34,8 @@ interface MessagePropertiesBaseInterface extends NodePropertiesBaseInterface {
public const PROPERTY_BCC = 'bcc';
public const PROPERTY_SUBJECT = 'subject';
public const PROPERTY_BODY = 'body';
public const PROPERTY_BODY_TEXT_PLAIN = 'bodyTextPlain';
public const PROPERTY_BODY_TEXT_HTML = 'bodyTextHtml';
public const PROPERTY_ATTACHMENTS = 'attachments';
public const PROPERTY_FLAGS = 'flags';
public const PROPERTY_TAGS = 'tags';
@@ -215,7 +217,7 @@ interface MessagePropertiesBaseInterface extends NodePropertiesBaseInterface {
*
* @since 2025.05.01
*
* @return array<int,MessagePartInterface>
* @return array<int,AttachmentInterface>
*/
public function getAttachments(): array;
@@ -10,7 +10,6 @@ declare(strict_types=1);
namespace KTXF\Mail\Object;
use DateTimeImmutable;
use DateTimeInterface;
/**
* Abstract Message Properties Mutable Class
@@ -23,90 +22,6 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
protected string $type = 'mail.message';
/**
* @param array<int,AddressInterface> $values
*
* @return array<int,array<string,string|null>>
*/
protected function normalizeAddresses(array $values): array {
return array_map(
static fn(AddressInterface $value): array => $value->toArray(),
$values,
);
}
protected function normalizeDate(?DateTimeImmutable $value): ?string {
return $value?->format(DATE_ATOM);
}
/**
* @param array<int,MessagePartInterface> $values
*
* @return array<int,array<string,mixed>>
*/
protected function normalizeParts(array $values): array {
return array_map(
static fn(MessagePartInterface $value): array => $value->jsonSerialize(),
$values,
);
}
protected function buildBodyPartFromContent(?string $textPlain, ?string $textHtml): ?MessagePart {
$hasTextPlain = is_string($textPlain) && $textPlain !== '';
$hasTextHtml = is_string($textHtml) && $textHtml !== '';
if (!$hasTextPlain && !$hasTextHtml) {
return null;
}
if ($hasTextPlain && $hasTextHtml) {
return MessagePart::fromArray([
'partId' => 'body',
'type' => 'multipart/alternative',
'subParts' => [
[
'partId' => 'body.text',
'type' => 'text/plain',
'charset' => 'utf-8',
'content' => $textPlain,
'disposition' => 'inline',
],
[
'partId' => 'body.html',
'type' => 'text/html',
'charset' => 'utf-8',
'content' => $textHtml,
'disposition' => 'inline',
],
],
]);
}
return MessagePart::fromArray([
'partId' => 'body',
'type' => $hasTextHtml ? 'text/html' : 'text/plain',
'charset' => 'utf-8',
'content' => $hasTextHtml ? $textHtml : $textPlain,
'disposition' => 'inline',
]);
}
protected function updateBodyContent(string $type, ?string $value): static {
$currentPlain = $this->getBodyTextPlain();
$currentHtml = $this->getBodyTextHtml();
if ($type === 'text/plain') {
$currentPlain = $value;
} else {
$currentHtml = $value;
}
$this->data[static::PROPERTY_BODY] = $this->buildBodyPartFromContent($currentPlain, $currentHtml)?->jsonSerialize();
return $this;
}
/**
* @inheritDoc
*/
@@ -176,16 +91,16 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
/**
* @inheritDoc
*/
public function setReceived(?DateTimeInterface $value): static {
$this->data[static::PROPERTY_RECEIVED] = $this->normalizeDate($value);
public function setReceived(?DateTimeImmutable $value): static {
$this->data[static::PROPERTY_RECEIVED] = $value;
return $this;
}
/**
* @inheritDoc
*/
public function setSent(?DateTimeInterface $value): static {
$this->data[static::PROPERTY_SENT] = $this->normalizeDate($value);
public function setSent(DateTimeImmutable $value): static {
$this->data[static::PROPERTY_SENT] = $value;
return $this;
}
@@ -193,7 +108,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setSender(?AddressInterface $value): static {
$this->data[static::PROPERTY_SENDER] = $value?->toArray();
$this->data[static::PROPERTY_SENDER] = $value;
return $this;
}
@@ -201,7 +116,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setFrom(AddressInterface $value): static {
$this->data[static::PROPERTY_FROM] = $value->toArray();
$this->data[static::PROPERTY_FROM] = $value;
return $this;
}
@@ -209,7 +124,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setReplyTo(AddressInterface ...$value): static {
$this->data[static::PROPERTY_REPLY_TO] = $this->normalizeAddresses($value);
$this->data[static::PROPERTY_REPLY_TO] = $value;
return $this;
}
@@ -217,7 +132,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setTo(AddressInterface ...$value): static {
$this->data[static::PROPERTY_TO] = $this->normalizeAddresses($value);
$this->data[static::PROPERTY_TO] = $value;
return $this;
}
@@ -225,7 +140,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setCc(AddressInterface ...$value): static {
$this->data[static::PROPERTY_CC] = $this->normalizeAddresses($value);
$this->data[static::PROPERTY_CC] = $value;
return $this;
}
@@ -233,7 +148,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setBcc(AddressInterface ...$value): static {
$this->data[static::PROPERTY_BCC] = $this->normalizeAddresses($value);
$this->data[static::PROPERTY_BCC] = $value;
return $this;
}
@@ -249,7 +164,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setBody(?MessagePartInterface $value): static {
$this->data[static::PROPERTY_BODY] = $value?->jsonSerialize();
$this->data[static::PROPERTY_BODY] = $value;
return $this;
}
@@ -257,29 +172,31 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc
*/
public function setBodyTextPlain(?string $value): static {
return $this->updateBodyContent('text/plain', $value);
}
/**
* @inheritDoc
*/
public function setBodyTextHtml(?string $value): static {
return $this->updateBodyContent('text/html', $value);
}
/**
* @inheritDoc
*/
public function setAttachments(\KTXF\Mail\Object\MessagePartInterface ...$value): static {
$this->data[static::PROPERTY_ATTACHMENTS] = $this->normalizeParts($value);
$this->data[static::PROPERTY_BODY_TEXT_PLAIN] = $value;
return $this;
}
/**
* @inheritDoc
*/
public function addAttachment(\KTXF\Mail\Object\MessagePartInterface $value): static {
$this->data[static::PROPERTY_ATTACHMENTS][] = $value->jsonSerialize();
public function setBodyTextHtml(?string $value): static {
$this->data[static::PROPERTY_BODY_TEXT_HTML] = $value;
return $this;
}
/**
* @inheritDoc
*/
public function setAttachments(AttachmentInterface ...$value): static {
$this->data[static::PROPERTY_ATTACHMENTS] = $value;
return $this;
}
/**
* @inheritDoc
*/
public function addAttachment(AttachmentInterface $value): static {
$this->data[static::PROPERTY_ATTACHMENTS][] = $value;
return $this;
}
@@ -295,7 +212,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
public function setFlags(array $value): static {
if (!isset($this->data[static::PROPERTY_FLAGS])) {
$this->data[static::PROPERTY_FLAGS] = [
'seen' => false,
'read' => false,
'starred' => false,
'important' => false,
'answered' => false,
@@ -315,7 +232,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
public function setFlag(string $name, bool $value): static {
if (!isset($this->data[static::PROPERTY_FLAGS])) {
$this->data[static::PROPERTY_FLAGS] = [
'seen' => false,
'read' => false,
'starred' => false,
'important' => false,
'answered' => false,
@@ -223,22 +223,22 @@ interface MessagePropertiesMutableInterface extends MessagePropertiesBaseInterfa
*
* @since 2025.05.01
*
* @param MessagePartInterface ...$value
* @param AttachmentInterface ...$value
*
* @return self
*/
public function setAttachments(MessagePartInterface ...$value): static;
public function setAttachments(AttachmentInterface ...$value): static;
/**
* Adds an attachment
*
* @since 2025.05.01
*
* @param MessagePartInterface $value
* @param AttachmentInterface $value
*
* @return self
*/
public function addAttachment(MessagePartInterface $value): static;
public function addAttachment(AttachmentInterface $value): static;
/**
* Sets message flags
@@ -13,15 +13,15 @@ use KTXF\Mail\Service\ServiceBaseInterface;
use KTXF\Resource\Provider\ResourceProviderBaseInterface;
/**
* Provider Base Interface
* Mail Provider Base Interface
*
* Core interface for providers with context-aware service discovery.
* Core interface for mail providers with context-aware service discovery.
*
* @since 2025.05.01
*/
interface ProviderBaseInterface extends ResourceProviderBaseInterface{
public const JSON_TYPE = 'mail:provider';
public const JSON_TYPE = 'mail.provider';
/**
* Finds a service that handles a specific email address
@@ -12,11 +12,11 @@ namespace KTXF\Mail\Provider;
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
/**
* Provider Autodiscovery Interface
* Mail Provider Autodiscovery Interface
*
* Optional interface for providers that support automatic service discovery
* Optional interface for mail providers that support automatic service discovery
* from email addresses or domains. Providers implementing this interface can
* discover service configurations using various methods specific to their
* discover mail service configurations using various methods specific to their
* protocol or provider type.
*
* Examples:
@@ -28,8 +28,6 @@ use KTXF\Resource\Provider\ResourceServiceLocationInterface;
*/
interface ProviderServiceDiscoverInterface {
public const CAPABILITY_SERVICE_DISCOVER = 'ServiceDiscover';
/**
* Attempts to discover service configuration using provider-specific methods.
*
@@ -12,9 +12,11 @@ namespace KTXF\Mail\Provider;
use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
/**
* Provider Service Mutate Interface
* Mail Provider Service Mutate Interface
*
* Optional interface for providers that support service CRUD operations
* Optional interface for providers that support service CRUD operations.
*
* Implementations return ServiceMutableInterface instances (which extend ResourceServiceMutateInterface).
*
* @since 2025.05.01
*
@@ -26,5 +28,6 @@ use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
interface ProviderServiceMutateInterface extends ResourceProviderServiceMutateInterface {
// Methods inherited from ResourceProviderServiceMutateInterface
// Implementations should return/accept ServiceMutableInterface instances
}
@@ -13,20 +13,30 @@ use KTXF\Mail\Service\ServiceBaseInterface;
use KTXF\Mail\Service\ServiceMutableInterface;
/**
* Provider Service Test Interface
* Mail Provider Service Test Interface
*
* Optional interface for providers that support testing service connections
* Optional interface for mail providers that support testing service connections.
* Providers implementing this interface can validate connection parameters,
* test authentication, and verify service availability before creating a
* persistent service configuration.
*
* Supports two testing modes:
* 1. Testing an existing service (validate current configuration)
* 2. Testing a fresh configuration (validate before saving)
*
* @since 2025.05.01
*/
interface ProviderServiceTestInterface {
public const CAPABILITY_SERVICE_TEST = 'ServiceTest';
/**
* Test a service connection
*
* Tests connectivity, authentication, and capabilities of a service
* Tests connectivity, authentication, and capabilities of a service.
*
* For new services: use serviceFresh() to create a service, configure it with
* setters, then pass it to this method for testing before persisting.
*
* For existing services: fetch the service and pass it directly.
*
* @since 2025.05.01
*
@@ -39,8 +49,28 @@ interface ProviderServiceTestInterface {
*
* @return array Test results in the format:
* [
* 'disposition' => 'Success' | 'Failure' | 'Warning',
* 'success' => bool,
* 'message' => 'Connection successful' | 'Error message',
* 'details' => [
* 'connected' => bool, // Socket/HTTP connection succeeded
* 'authenticated' => bool, // Authentication succeeded
* 'capabilities' => ['IMAP4rev1', ...], // Server capabilities (if applicable)
* 'serverInfo' => 'Server version/banner',
* 'latency' => 123, // Connection time in milliseconds
* 'protocols' => [
* 'inbound' => [
* 'connected' => bool,
* 'authenticated' => bool,
* 'error' => 'error message if failed'
* ],
* 'outbound' => [ // For split-socket (IMAP+SMTP)
* 'connected' => bool,
* 'authenticated' => bool,
* 'error' => 'error message if failed'
* ]
* ],
* 'errors' => ['Error 1', 'Error 2'], // List of errors encountered
* ]
* ]
*/
public function serviceTest(ServiceBaseInterface|ServiceMutableInterface $service, array $options = []): array;
@@ -11,21 +11,19 @@ namespace KTXF\Mail\Service;
use Generator;
use KTXF\Mail\Collection\CollectionBaseInterface;
use KTXF\Mail\Entity\EntityBaseInterface;
use KTXF\Mail\Object\AddressInterface;
use KTXF\Resource\BinaryResource;
use KTXF\Resource\Delta\Delta;
use KTXF\Resource\Filter\IFilter;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
use KTXF\Resource\Provider\ResourceServiceBaseInterface;
use KTXF\Resource\Range\IRange;
use KTXF\Resource\Range\RangeType;
use KTXF\Resource\Sort\ISort;
/**
* Service Base Interface
* Mail Service Base Interface
*
* Minimum interface for a service, providing read-only access to collections and entities.
* Core interface for mail services with full protocol support (IMAP, JMAP, EWS, ActiveSync, Gmail API, etc.)
* Provides identity, addressing, capability information, and collection/message operations.
*
* @since 2025.05.01
*/
@@ -72,9 +70,9 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public const CAPABILITY_ENTITY_SORT_DATE_SENT = 'sent';
public const CAPABILITY_ENTITY_SORT_SIZE = 'size';
public const JSON_TYPE = 'mail:service';
public const PROPERTY_PRIMARY_ADDRESS = 'primaryAddress';
public const PROPERTY_SECONDARY_ADDRESSES = 'secondaryAddresses';
public const JSON_TYPE = 'mail.service';
public const JSON_PROPERTY_PRIMARY_ADDRESS = 'primaryAddress';
public const JSON_PROPERTY_SECONDARY_ADDRESSES = 'secondaryAddresses';
/**
* Gets the primary mailing address for this service
@@ -113,7 +111,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order
*
* @return array<string|int,CollectionBaseInterface> Collections indexed by identifier
* @return array<string|int,CollectionBaseInterface> Collections indexed by ID
*/
public function collectionList(string|int $location, ?IFilter $filter = null, ?ISort $sort = null): array;
@@ -140,9 +138,9 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
*
* @since 2025.05.01
*
* @param string|int ...$identifiers Collection identifiers to check
* @param string|int ...$identifiers Collection IDs to check
*
* @return array<string|int,bool> Map of identifier => exists
* @return array<string|int,bool> Map of ID => exists
*/
public function collectionExtant(string|int $location, string|int ...$identifiers): array;
@@ -151,44 +149,44 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
*
* @since 2025.05.01
*
* @param string|int $identifier Collection identifier
* @param string|int $identifier Collection ID
*
* @return CollectionBaseInterface|null Collection or null if not found
*/
public function collectionFetch(string|int $identifier): ?CollectionBaseInterface;
/**
* Lists entities in a collection
* Lists messages in a collection
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string|int $collection Collection ID
* @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination
* @param array|null $properties Optional entity properties to fetch
* @param array|null $properties Optional message properties to fetch
*
* @return array<string,EntityBaseInterface> Entities indexed by Urn
* @return array<string|int,EntityBaseInterface> Messages indexed by ID
*/
public function entityListBulk(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array;
public function entityList(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array;
/**
* Lists entities in a collection
* Lists messages in a collection
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string|int $collection Collection ID
* @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination
* @param array|null $properties Optional entity properties to fetch
* @param array|null $properties Optional message properties to fetch
*
* @return Generator Yields entities one by one as EntityBaseInterface
* @return Generator Yields messages one by one as EntityBaseInterface
*/
public function entityListStream(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): Generator;
/**
* Creates a filter builder for entities
* Creates a filter builder for messages
*
* @since 2025.05.01
*
@@ -197,7 +195,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListFilter(): IFilter;
/**
* Creates a sort builder for entities
* Creates a sort builder for messages
*
* @since 2025.05.01
*
@@ -206,7 +204,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListSort(): ISort;
/**
* Creates a range builder for entities
* Creates a range builder for messages
*
* @since 2025.05.01
*
@@ -217,60 +215,40 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListRange(RangeType $type): IRange;
/**
* Gets incremental changes since last signature
* Gets incremental changes since last sync
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string $signature Token from previous delta
* @param string|int $collection Collection ID
* @param string $signature Sync token from previous sync
* @param string $detail Detail level: 'ids', 'minimal', 'full'
*
* @return array ['signature' => string, 'added' => array, 'modified' => array, 'removed' => array]
*/
public function entityDelta(string|int $collection, string $signature): Delta;
public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta;
/**
* Checks if entities exist
* Checks if messages exist
*
* @since 2025.05.01
*
* @param string|int $collection Collection identifier
* @param string|int ...$identifiers Entity identifiers to check
* @param string|int $collection Collection ID
* @param string|int ...$identifiers Message IDs to check
*
* @return array<string|int,bool> Map of identifier => exists
* @return array<string|int,bool> Map of ID => exists
*/
public function entityExtant(string|int $collection, string|int ...$identifiers): array;
/**
* Fetches one or more entities as an array
* Fetches one or more entities
*
* @since 2025.05.01
*
* @param EntityIdentifierInterface ...$identifiers Entity identifiers to fetch
* @param string|int $collection Collection ID
* @param string|int ...$identifiers Message IDs to fetch
*
* @return array<string,EntityBaseInterface> Entities indexed by Urn
* @return array<string|int,EntityBaseInterface> Messages indexed by ID
*/
public function entityFetchBulk(EntityIdentifierInterface ...$identifiers): array;
public function entityFetch(string|int $collection, string|int ...$identifiers): array;
/**
* Fetches one or more entities as a stream
*
* @since 2025.05.01
*
* @param EntityIdentifierInterface ...$identifiers Entity identifiers to fetch
*
* @return Generator<string,EntityBaseInterface> Yields entities one by one
*/
public function entityFetchStream(EntityIdentifierInterface ...$identifiers): Generator;
/**
* Downloads an entity or part of an entity
*
* @since 2025.05.01
*
* @param EntityIdentifierInterface $target Entity identifier
* @param array|null $part Optional part to download
*
* @return BinaryResource
*/
public function entityDownload(EntityIdentifierInterface $target, array|null $part): BinaryResource;
}
@@ -15,9 +15,10 @@ use KTXF\Mail\Collection\CollectionPropertiesBaseInterface;
use KTXF\Resource\Identifier\CollectionIdentifier;
/**
* Service Collection Mutable Interface
* Mail Service Collection Mutable Interface
*
* Optional interface for services that support collection CRUD operations
* Optional interface for services that support collection CRUD operations.
* Provides mailbox/folder creation, modification, deletion, and moving.
*
* @since 2025.05.01
*/
@@ -12,9 +12,10 @@ namespace KTXF\Mail\Service;
use KTXF\Resource\Provider\ResourceServiceConfigureInterface;
/**
* Service Configurable Interface
* Mail Service Configurable Interface
*
* Extends base service interface with setter methods for mutable properties
* Extends base service interface with setter methods for mutable properties.
* Used for service configuration and updates.
*
* @since 2025.05.01
*/
@@ -16,9 +16,10 @@ use KTXF\Resource\Identifier\CollectionIdentifier;
use KTXF\Resource\Identifier\EntityIdentifier;
/**
* Service Entity Mutable Interface
* Mail Service Entity Mutable Interface
*
* Optional interface for services that support entity CRUD operations
* Optional interface for services that support entity CRUD operations.
* Provides entity creation, modification, deletion, copying, moving, and flag management.
*
* @since 2025.05.01
*/
@@ -65,20 +66,6 @@ interface ServiceEntityMutableInterface {
*/
public function entityModify(EntityIdentifier $target, MessagePropertiesMutableInterface $properties): EntityBaseInterface;
/**
* Patches an existing entity(ies) with partial data
*
* @since 2025.05.01
*
* @param MessagePropertiesMutableInterface $properties Partial entity properties
* @param EntityIdentifier ...$targets Source entities to patch
*
* @return array<string, array{
* disposition: 'patched'|'error'
* }> Results keyed by source entity identifier
*/
public function entityPatch(MessagePropertiesMutableInterface $properties, EntityIdentifier ...$targets): array;
/**
* Deletes entities
*
@@ -94,6 +81,20 @@ interface ServiceEntityMutableInterface {
*/
public function entityDelete(EntityIdentifier ...$targets): array;
/**
* Patches an existing entity(ies) with partial data
*
* @since 2025.05.01
*
* @param MessagePropertiesMutableInterface $properties Partial entity properties
* @param EntityIdentifier ...$targets Source entities to patch
*
* @return array<string, array{
* disposition: 'patched'|'error'
* }> Results keyed by source entity identifier
*/
public function entityPatch(MessagePropertiesMutableInterface $properties, EntityIdentifier ...$targets): array;
/**
* Moves entities to another collection
*
@@ -1,54 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Mail\Service;
use KTXF\Mail\Entity\EntityMutableInterface;
use KTXF\Mail\Object\AddressInterface;
use KTXF\Mail\Object\MessagePropertiesMutableInterface;
use KTXF\Mail\Submission\EntitySubmitResult;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
/**
* Mail Service Submit Interface
*
* Interface for mail services capable of outbound submission.
* Supports submission of fresh in-memory messages and already-existing
* provider-backed drafts through a single provider-agnostic contract.
*
* @since 2026.06.07
*/
interface ServiceEntitySubmitInterface {
public const CAPABILITY_ENTITY_SUBMIT_FRESH = 'EntitySubmitFresh';
public const CAPABILITY_ENTITY_SUBMIT_DRAFT = 'EntitySubmitDraft';
/**
* Creates a fresh entity instance for composition
*
* @since 2025.05.01
*
* @return EntityMutableInterface Fresh entity object
*/
public function entityFresh(): EntityMutableInterface;
/**
* Submits an outbound message.
*
* @since 2026.06.07
*
* @param AddressInterface $sender Sender address
* @param EntityIdentifierInterface|null $source Source entity identifier
* @param MessagePropertiesMutableInterface|null $message Message properties
*
* @return EntitySubmitResult Normalized submission result
*/
public function entitySubmit(AddressInterface $sender, EntityIdentifierInterface|null $source = null, MessagePropertiesMutableInterface|null $message = null): EntitySubmitResult;
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Mail\Service;
use KTXF\Mail\Entity\EntityMutableInterface;
use KTXF\Mail\Exception\SendException;
/**
* Mail Service Transmit Interface
*
* Interface for mail services capable of transmitting outbound entities.
*
* @since 2025.05.01
*/
interface ServiceEntityTransmitInterface {
public const CAPABILITY_ENTITY_TRANSMIT = 'EntityTransmit';
/**
* Creates a fresh entity instance for composition
*
* @since 2025.05.01
*
* @return EntityMutableInterface Fresh entity object
*/
public function entityFresh(): EntityMutableInterface;
/**
* Transmits an outbound entity
*
* @since 2025.05.01
*
* @param EntityMutableInterface $entity Entity to transmit
*
* @return string Entity identifier assigned by the transport
*
* @throws SendException On delivery failure
*/
public function entitySend(EntityMutableInterface $entity): string;
}
@@ -13,9 +13,10 @@ use KTXF\Mail\Object\AddressInterface;
use KTXF\Resource\Provider\ResourceServiceMutateInterface;
/**
* Service Mutable Interface
* Mail Service Mutable Interface
*
* Extends base service interface with setter methods for mutable properties
* Extends base service interface with setter methods for mutable properties.
* Used for service configuration and updates.
*
* @since 2025.05.01
*/
@@ -1,52 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace KTXF\Mail\Submission;
use InvalidArgumentException;
use JsonSerializable;
use KTXF\Resource\Identifier\EntityIdentifier;
/**
* Entity Submit Result
*
* Normalized result for outbound submission.
*
* @since 2026.06.07
*/
final class EntitySubmitResult implements JsonSerializable {
public const DISPOSITION_SENT = 'sent';
public const DISPOSITION_ERROR = 'error';
public function __construct(
public readonly string $disposition,
public readonly ?string $transportId = null,
public readonly ?EntityIdentifier $sourceDraft = null,
public readonly ?EntityIdentifier $sentEntity = null,
public readonly ?string $errorCode = null,
public readonly ?string $errorMessage = null,
) {
if (!in_array($this->disposition, [self::DISPOSITION_SENT, self::DISPOSITION_ERROR], true)) {
throw new InvalidArgumentException('Invalid submission disposition: ' . $this->disposition);
}
}
public function jsonSerialize(): array {
return array_filter([
'disposition' => $this->disposition,
'transportId' => $this->transportId,
'sourceDraft' => $this->sourceDraft !== null ? (string)$this->sourceDraft : null,
'sentEntity' => $this->sentEntity !== null ? (string)$this->sentEntity : null,
'errorCode' => $this->errorCode,
'errorMessage' => $this->errorMessage,
], static fn(mixed $value): bool => $value !== null);
}
}
@@ -41,7 +41,7 @@ abstract class CollectionMutableAbstract extends NodeMutableAbstract implements
// Copy all property values
$this->properties->setLabel($value->getLabel());
$this->properties->setDescription($value->getDescription());
$this->properties->setRank($value->getRank());
$this->properties->setPriority($value->getPriority());
$this->properties->setVisibility($value->getVisibility());
$this->properties->setColor($value->getColor());
@@ -20,15 +20,17 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
*/
abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements CollectionPropertiesBaseInterface {
public const JSON_TYPE = CollectionPropertiesBaseInterface::JSON_TYPE;
/**
* @inheritDoc
*/
public function content(): array {
$content = $this->data[self::PROPERTY_CONTENT] ?? null;
$content = $this->data[self::JSON_PROPERTY_CONTENT] ?? null;
if (is_array($content)) {
return array_filter(array_map(function ($item) {
return $item instanceof CollectionContent ? $item : CollectionContent::tryFrom($item);
}, $content));
return array_map(function ($item) {
return new CollectionContent($item);
}, $content);
} else {
return [];
}
@@ -38,35 +40,35 @@ abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstra
* @inheritDoc
*/
public function getLabel(): string {
return $this->data[self::PROPERTY_LABEL] ?? '';
return $this->data[self::JSON_PROPERTY_LABEL] ?? '';
}
/**
* @inheritDoc
*/
public function getDescription(): ?string {
return $this->data[self::PROPERTY_DESCRIPTION] ?? null;
return $this->data[self::JSON_PROPERTY_DESCRIPTION] ?? null;
}
/**
* @inheritDoc
*/
public function getRank(): ?int {
return $this->data[self::PROPERTY_RANK] ?? null;
public function getPriority(): ?int {
return $this->data[self::JSON_PROPERTY_PRIORITY] ?? null;
}
/**
* @inheritDoc
*/
public function getVisibility(): ?bool {
return $this->data[self::PROPERTY_VISIBILITY] ?? null;
return $this->data[self::JSON_PROPERTY_VISIBILITY] ?? null;
}
/**
* @inheritDoc
*/
public function getColor(): ?string {
return $this->data[self::PROPERTY_COLOR] ?? null;
return $this->data[self::JSON_PROPERTY_COLOR] ?? null;
}
}
@@ -10,17 +10,18 @@ declare(strict_types=1);
namespace KTXF\People\Collection;
use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
use PhpParser\Node\Expr\Array_;
interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'people:collection';
public const PROPERTY_CONTENT = 'content';
public const JSON_PROPERTY_CONTENT = 'content';
public const PROPERTY_LABEL = 'label';
public const PROPERTY_DESCRIPTION = 'description';
public const PROPERTY_RANK = 'rank';
public const PROPERTY_VISIBILITY = 'visibility';
public const PROPERTY_COLOR = 'color';
public const JSON_PROPERTY_LABEL = 'label';
public const JSON_PROPERTY_DESCRIPTION = 'description';
public const JSON_PROPERTY_PRIORITY = 'priority';
public const JSON_PROPERTY_VISIBILITY = 'visibility';
public const JSON_PROPERTY_COLOR = 'color';
/**
* Gets the content type of this collection
@@ -46,11 +47,11 @@ interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface
public function getDescription(): ?string;
/**
* Gets the rank of this collection
* Gets the priority of this collection
*
* @since 2025.05.01
*/
public function getRank(): ?int;
public function getPriority(): ?int;
/**
* Gets the visibility of this collection
@@ -33,7 +33,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setLabel(string $value): static {
$this->data[self::PROPERTY_LABEL] = $value;
$this->data[self::JSON_PROPERTY_LABEL] = $value;
return $this;
}
@@ -41,15 +41,15 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setDescription(?string $value): static {
$this->data[self::PROPERTY_DESCRIPTION] = $value;
$this->data[self::JSON_PROPERTY_DESCRIPTION] = $value;
return $this;
}
/**
* @inheritDoc
*/
public function setRank(?int $value): static {
$this->data[self::PROPERTY_RANK] = $value;
public function setPriority(?int $value): static {
$this->data[self::JSON_PROPERTY_PRIORITY] = $value;
return $this;
}
@@ -57,7 +57,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setVisibility(?bool $value): static {
$this->data[self::PROPERTY_VISIBILITY] = $value;
$this->data[self::JSON_PROPERTY_VISIBILITY] = $value;
return $this;
}
@@ -65,7 +65,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc
*/
public function setColor(?string $value): static {
$this->data[self::PROPERTY_COLOR] = $value;
$this->data[self::JSON_PROPERTY_COLOR] = $value;
return $this;
}
@@ -30,11 +30,11 @@ interface CollectionPropertiesMutableInterface extends NodePropertiesMutableInte
public function setDescription(?string $value): static;
/**
* Sets the rank of this collection
* Sets the priority of this collection
*
* @since 2025.05.01
*/
public function setRank(?int $value): static;
public function setPriority(?int $value): static;
/**
* Sets the visibility of this collection
@@ -9,7 +9,6 @@ declare(strict_types=1);
namespace KTXF\People\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeBaseAbstract;
/**
@@ -21,12 +20,7 @@ use KTXF\Resource\Provider\Node\NodeBaseAbstract;
*/
abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface {
protected string $type = 'people:entity';
protected EntityPropertiesBaseInterface $properties;
protected function nodeIdentifier(): EntityIdentifier {
return new EntityIdentifier($this->data[static::PROPERTY_PROVIDER], $this->data[static::PROPERTY_SERVICE], $this->data[static::PROPERTY_COLLECTION], $this->data[static::PROPERTY_IDENTIFIER]);
}
protected EntityPropertiesBaseAbstract $properties;
/**
* @inheritDoc
@@ -13,6 +13,8 @@ use KTXF\Resource\Provider\Node\NodeBaseInterface;
interface EntityBaseInterface extends NodeBaseInterface {
public const JSON_TYPE = 'people:entity';
/**
* Gets the entity properties
*
@@ -9,7 +9,6 @@ declare(strict_types=1);
namespace KTXF\People\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeMutableAbstract;
use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
@@ -22,12 +21,9 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
*/
abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface {
protected string $type = 'people:entity';
protected EntityPropertiesMutableInterface $properties;
public const JSON_TYPE = EntityMutableInterface::JSON_TYPE;
protected function nodeIdentifier(): EntityIdentifier {
return new EntityIdentifier($this->data[static::PROPERTY_PROVIDER], $this->data[static::PROPERTY_SERVICE], (string) $this->data[static::PROPERTY_COLLECTION], (string) $this->data[static::PROPERTY_IDENTIFIER]);
}
protected EntityPropertiesMutableAbstract $properties;
/**
* @inheritDoc
@@ -44,7 +40,7 @@ abstract class EntityMutableAbstract extends NodeMutableAbstract implements Enti
throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface');
}
$this->properties = $value;
$this->properties->setDataRaw($value->getDataRaw());
return $this;
}
@@ -16,6 +16,8 @@ use KTXF\Resource\Provider\Node\NodeMutableInterface;
*/
interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface {
public const JSON_TYPE = EntityBaseInterface::JSON_TYPE;
/**
* Gets the entity properties (mutable)
*
@@ -13,10 +13,9 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface {
/**
* @inheritDoc
*/
public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE;
public function getDataRaw(): array|string|null {
return $this->data[self::PROPERTY_DATA] ?? null;
return $this->data[self::JSON_PROPERTY_DATA] ?? null;
}
}
@@ -14,7 +14,7 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'people:entity';
public const PROPERTY_DATA = 'data';
public const JSON_PROPERTY_DATA = 'data';
public function getDataRaw(): array|string|null;

Some files were not shown because too many files have changed in this diff Show More