38 Commits

Author SHA1 Message Date
Sebastian 3e20439c64 chore(deps): update dependency mongodb/mongodb to v2.3.0
PHP Unit Tests / test (pull_request) Failing after 13m24s
JS Unit Tests / test (pull_request) Failing after 13m26s
Build Test / build (pull_request) Failing after 13m54s
2026-07-02 03:02:53 +00:00
Sebastian 8f61c4d2d5 refactor: chrono objects
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-28 17:51:23 -04:00
Sebastian 319fcb6485 Merge pull request 'chore(deps): update dependency prettier to v3.8.4' (#107) from renovate/prettier-3.x-lockfile into main
Reviewed-on: #107
2026-06-28 14:42:46 +00:00
Sebastian 18478f9985 Merge pull request 'chore(deps): update dependency phpseclib/phpseclib to v3.0.55' (#106) from renovate/phpseclib-phpseclib-3.x-lockfile into main
Reviewed-on: #106
2026-06-28 14:42:36 +00:00
Sebastian 139b4df844 refactor: use seen instead of read
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-25 23:47:52 -04:00
Sebastian a7ad86860d refactor: use unified service provider desing
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-25 23:47:24 -04:00
Sebastian 6ca83a7dd7 refactor: use unified service provider desing
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-25 23:47:17 -04:00
Sebastian 6a3e1f309e chore(deps): update dependency prettier to v3.8.4
PHP Unit Tests / test (pull_request) Failing after 13m54s
JS Unit Tests / test (pull_request) Failing after 13m56s
Build Test / build (pull_request) Failing after 14m6s
2026-06-25 03:02:46 +00:00
Sebastian 4d4999602c chore(deps): update dependency phpseclib/phpseclib to v3.0.55
PHP Unit Tests / test (pull_request) Failing after 14m35s
JS Unit Tests / test (pull_request) Failing after 14m37s
Build Test / build (pull_request) Failing after 14m45s
2026-06-25 03:02:45 +00:00
Sebastian 87d6af93b7 Merge pull request 'chore(deps): update dependency dompurify to v3.4.11' (#104) from renovate/dompurify-3.x-lockfile into main
Reviewed-on: #104
2026-06-24 15:23:15 +00:00
Sebastian 2b44d395f0 Merge pull request 'chore(deps): update dependency eslint-plugin-vue to v10.9.2' (#105) from renovate/eslint-plugin-vue-10.x-lockfile into main
Reviewed-on: #105
2026-06-24 15:23:06 +00:00
Sebastian 8ca85e894d chore(deps): update dependency eslint-plugin-vue to v10.9.2
PHP Unit Tests / test (pull_request) Failing after 14m11s
JS Unit Tests / test (pull_request) Failing after 14m15s
Build Test / build (pull_request) Failing after 14m31s
2026-06-24 03:03:06 +00:00
Sebastian cc8482f309 chore(deps): update dependency dompurify to v3.4.11
Build Test / build (pull_request) Successful in 23s
PHP Unit Tests / test (pull_request) Failing after 14m42s
JS Unit Tests / test (pull_request) Failing after 14m44s
2026-06-24 03:03:02 +00:00
Sebastian 14f25c9a44 refactor: people entity properties objects
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-21 21:47:51 -04:00
Sebastian b6c3551d36 refactor: priority to rank
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-21 21:46:30 -04:00
Sebastian a6e2a447e5 refactor: people interfaces to unified design
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-20 19:04:39 -04:00
Sebastian 802849a60d refactor: docs
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-20 19:04:05 -04:00
Sebastian a3443b1e54 fix: size property constant
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-16 18:03:18 -04:00
Sebastian cededf82c3 Merge pull request 'refactor: object property names' (#103) from refactor/object-property-names into main
Reviewed-on: #103
2026-06-16 17:21:55 +00:00
Sebastian 388e3fb8b8 refactor: object property names
Build Test / build (pull_request) Successful in 14s
JS Unit Tests / test (pull_request) Successful in 13s
PHP Unit Tests / test (pull_request) Successful in 35s
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-16 13:19:40 -04:00
Sebastian bc54ba5811 Merge pull request 'refactor: message object properties' (#102) from refactor/message-object-properties into main
Reviewed-on: #102
2026-06-16 17:17:02 +00:00
Sebastian 869f2e4fb4 refactor: message object properties
JS Unit Tests / test (pull_request) Successful in 23s
Build Test / build (pull_request) Successful in 26s
PHP Unit Tests / test (pull_request) Successful in 52s
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-06-16 13:12:30 -04:00
Sebastian 6dadd13acf Merge pull request 'chore(deps): update dependency @vue/test-utils to v2.4.11' (#100) from renovate/vue-test-utils-2.x-lockfile into main
Renovate / renovate (push) Failing after 1m24s
Reviewed-on: #100
2026-06-06 04:08:49 +00:00
Sebastian 2d1ee97afe Merge pull request 'chore(deps): update dependency dompurify to v3.4.8' (#101) from renovate/dompurify-3.x-lockfile into main
Reviewed-on: #101
2026-06-06 04:08:34 +00:00
Sebastian 8432f5a823 chore(deps): update dependency dompurify to v3.4.8
JS Unit Tests / test (pull_request) Successful in 32s
Build Test / build (pull_request) Successful in 36s
PHP Unit Tests / test (pull_request) Successful in 1m0s
2026-06-05 03:05:06 +00:00
Sebastian 30cfd06594 chore(deps): update dependency @vue/test-utils to v2.4.11
Build Test / build (pull_request) Successful in 20s
JS Unit Tests / test (pull_request) Successful in 20s
PHP Unit Tests / test (pull_request) Successful in 58s
2026-06-05 03:05:04 +00:00
Sebastian 175981b5b2 Merge pull request 'chore(deps): update dependency dompurify to v3.4.5' (#96) from renovate/dompurify-3.x-lockfile into main
Renovate / renovate (push) Failing after 1m13s
Reviewed-on: #96
2026-05-29 03:06:09 +00:00
Sebastian 0e8c91e82d Merge pull request 'chore(deps): update dependency vite to v8.0.14' (#97) from renovate/vite-8.x-lockfile into main
Reviewed-on: #97
2026-05-29 03:05:59 +00:00
Sebastian 2fc9cbe033 Merge pull request 'chore(deps): update vitest monorepo to v4.1.7' (#98) from renovate/vitest-monorepo into main
Reviewed-on: #98
2026-05-29 03:05:49 +00:00
Sebastian 9deac1682e Merge pull request 'feat: mail entity download' (#99) from feat/mail-entity-download into main
Reviewed-on: #99
2026-05-29 03:05:38 +00:00
Sebastian 812a6680a8 feat: mail entity download
Build Test / build (pull_request) Successful in 20s
JS Unit Tests / test (pull_request) Successful in 18s
PHP Unit Tests / test (pull_request) Successful in 40s
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-05-28 23:02:37 -04:00
Sebastian a7f57aecf7 chore(deps): update vitest monorepo to v4.1.7
JS Unit Tests / test (pull_request) Successful in 23s
Build Test / build (pull_request) Successful in 26s
PHP Unit Tests / test (pull_request) Successful in 47s
2026-05-26 03:04:32 +00:00
Sebastian 9a234fb758 chore(deps): update dependency vite to v8.0.14
Build Test / build (pull_request) Successful in 31s
JS Unit Tests / test (pull_request) Successful in 30s
PHP Unit Tests / test (pull_request) Successful in 49s
2026-05-24 03:04:36 +00:00
Sebastian 87940f8eaf chore(deps): update dependency dompurify to v3.4.5
Build Test / build (pull_request) Successful in 23s
JS Unit Tests / test (pull_request) Successful in 32s
PHP Unit Tests / test (pull_request) Successful in 1m6s
2026-05-24 03:04:30 +00:00
Sebastian 8d716e3c72 Merge pull request 'refactor: list and fetch' (#95) from refactor/list-and-fetch into main
Renovate / renovate (push) Failing after 1m55s
Reviewed-on: #95
2026-05-17 21:58:01 +00:00
Sebastian 4e1d6d65d0 refactor: list and fetch
Build Test / build (pull_request) Successful in 13s
JS Unit Tests / test (pull_request) Successful in 12s
PHP Unit Tests / test (pull_request) Successful in 33s
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-05-17 17:57:44 -04:00
Sebastian a4a87dd82d Merge pull request 'chore(deps): update dependency eslint to v10.4.0' (#92) from renovate/eslint-monorepo into main
Renovate / renovate (push) Successful in 2m55s
Reviewed-on: #92
2026-05-16 04:16:19 +00:00
Sebastian f8fb84dd11 chore(deps): update dependency eslint to v10.4.0
JS Unit Tests / test (pull_request) Successful in 23s
Build Test / build (pull_request) Successful in 26s
PHP Unit Tests / test (pull_request) Successful in 1m4s
2026-05-16 03:02:33 +00:00
168 changed files with 1996 additions and 1417 deletions
Generated
+6 -6
View File
@@ -389,16 +389,16 @@
}, },
{ {
"name": "phpseclib/phpseclib", "name": "phpseclib/phpseclib",
"version": "3.0.52", "version": "3.0.55",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/phpseclib/phpseclib.git",
"reference": "2adaefc83df2ec548558307690f376dd7d4f4fce" "reference": "db9744e6d47e742b1f974e965ad49bdd041105af"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/2adaefc83df2ec548558307690f376dd7d4f4fce", "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db9744e6d47e742b1f974e965ad49bdd041105af",
"reference": "2adaefc83df2ec548558307690f376dd7d4f4fce", "reference": "db9744e6d47e742b1f974e965ad49bdd041105af",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -479,7 +479,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpseclib/phpseclib/issues", "issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.52" "source": "https://github.com/phpseclib/phpseclib/tree/3.0.55"
}, },
"funding": [ "funding": [
{ {
@@ -495,7 +495,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2026-04-27T07:02:15+00:00" "time": "2026-06-14T23:24:10+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
+150 -198
View File
@@ -459,9 +459,9 @@
} }
}, },
"node_modules/@eslint/config-helpers": { "node_modules/@eslint/config-helpers": {
"version": "0.5.5", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
"integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^1.2.1" "@eslint/core": "^1.2.1"
@@ -747,9 +747,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@oxc-project/types": { "node_modules/@oxc-project/types": {
"version": "0.130.0", "version": "0.132.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz",
"integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/Boshen" "url": "https://github.com/sponsors/Boshen"
@@ -878,9 +878,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -901,9 +898,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -924,9 +918,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -947,9 +938,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -970,9 +958,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -993,9 +978,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1101,9 +1083,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rolldown/binding-android-arm64": { "node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz",
"integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1117,9 +1099,9 @@
} }
}, },
"node_modules/@rolldown/binding-darwin-arm64": { "node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz",
"integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1133,9 +1115,9 @@
} }
}, },
"node_modules/@rolldown/binding-darwin-x64": { "node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz",
"integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1149,9 +1131,9 @@
} }
}, },
"node_modules/@rolldown/binding-freebsd-x64": { "node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz",
"integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1165,9 +1147,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm-gnueabihf": { "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz",
"integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1181,15 +1163,12 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm64-gnu": { "node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz",
"integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1200,15 +1179,12 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm64-musl": { "node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz",
"integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1219,15 +1195,12 @@
} }
}, },
"node_modules/@rolldown/binding-linux-ppc64-gnu": { "node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz",
"integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1238,15 +1211,12 @@
} }
}, },
"node_modules/@rolldown/binding-linux-s390x-gnu": { "node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz",
"integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1257,15 +1227,12 @@
} }
}, },
"node_modules/@rolldown/binding-linux-x64-gnu": { "node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz",
"integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1276,15 +1243,12 @@
} }
}, },
"node_modules/@rolldown/binding-linux-x64-musl": { "node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz",
"integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -1295,9 +1259,9 @@
} }
}, },
"node_modules/@rolldown/binding-openharmony-arm64": { "node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz",
"integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1311,9 +1275,9 @@
} }
}, },
"node_modules/@rolldown/binding-wasm32-wasi": { "node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz",
"integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==",
"cpu": [ "cpu": [
"wasm32" "wasm32"
], ],
@@ -1329,9 +1293,9 @@
} }
}, },
"node_modules/@rolldown/binding-win32-arm64-msvc": { "node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz",
"integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1345,9 +1309,9 @@
} }
}, },
"node_modules/@rolldown/binding-win32-x64-msvc": { "node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz",
"integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1724,14 +1688,14 @@
} }
}, },
"node_modules/@vitest/coverage-v8": { "node_modules/@vitest/coverage-v8": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.7.tgz",
"integrity": "sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ==", "integrity": "sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@bcoe/v8-coverage": "^1.0.2", "@bcoe/v8-coverage": "^1.0.2",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"ast-v8-to-istanbul": "^1.0.0", "ast-v8-to-istanbul": "^1.0.0",
"istanbul-lib-coverage": "^3.2.2", "istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1", "istanbul-lib-report": "^3.0.1",
@@ -1745,8 +1709,8 @@
"url": "https://opencollective.com/vitest" "url": "https://opencollective.com/vitest"
}, },
"peerDependencies": { "peerDependencies": {
"@vitest/browser": "4.1.6", "@vitest/browser": "4.1.7",
"vitest": "4.1.6" "vitest": "4.1.7"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@vitest/browser": { "@vitest/browser": {
@@ -1755,16 +1719,16 @@
} }
}, },
"node_modules/@vitest/expect": { "node_modules/@vitest/expect": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz",
"integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@standard-schema/spec": "^1.1.0", "@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2", "@types/chai": "^5.2.2",
"@vitest/spy": "4.1.6", "@vitest/spy": "4.1.7",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"chai": "^6.2.2", "chai": "^6.2.2",
"tinyrainbow": "^3.1.0" "tinyrainbow": "^3.1.0"
}, },
@@ -1773,13 +1737,13 @@
} }
}, },
"node_modules/@vitest/mocker": { "node_modules/@vitest/mocker": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz",
"integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/spy": "4.1.6", "@vitest/spy": "4.1.7",
"estree-walker": "^3.0.3", "estree-walker": "^3.0.3",
"magic-string": "^0.30.21" "magic-string": "^0.30.21"
}, },
@@ -1810,9 +1774,9 @@
} }
}, },
"node_modules/@vitest/pretty-format": { "node_modules/@vitest/pretty-format": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz",
"integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1823,13 +1787,13 @@
} }
}, },
"node_modules/@vitest/runner": { "node_modules/@vitest/runner": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz",
"integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"pathe": "^2.0.3" "pathe": "^2.0.3"
}, },
"funding": { "funding": {
@@ -1837,14 +1801,14 @@
} }
}, },
"node_modules/@vitest/snapshot": { "node_modules/@vitest/snapshot": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz",
"integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/pretty-format": "4.1.6", "@vitest/pretty-format": "4.1.7",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
"pathe": "^2.0.3" "pathe": "^2.0.3"
}, },
@@ -1853,9 +1817,9 @@
} }
}, },
"node_modules/@vitest/spy": { "node_modules/@vitest/spy": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz",
"integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
@@ -1863,13 +1827,13 @@
} }
}, },
"node_modules/@vitest/ui": { "node_modules/@vitest/ui": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.7.tgz",
"integrity": "sha512-wiu5em68DfGv/2HFvI1Njr7JI2CHcBlQvereSzVG8my53PRxjTNOCsD9VOkRKrsJBDHmyuXvosxWZw7T91a2mw==", "integrity": "sha512-TP6utB2yX6rsJNVRo2qAlsi48i1YwFTrLV2tnTtWqJaYX7m4lRCCLirZBjU6xC5m0RsPHr+L2+N+eIPhgEzFfw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"fflate": "^0.8.2", "fflate": "^0.8.2",
"flatted": "^3.4.2", "flatted": "^3.4.2",
"pathe": "^2.0.3", "pathe": "^2.0.3",
@@ -1881,17 +1845,17 @@
"url": "https://opencollective.com/vitest" "url": "https://opencollective.com/vitest"
}, },
"peerDependencies": { "peerDependencies": {
"vitest": "4.1.6" "vitest": "4.1.7"
} }
}, },
"node_modules/@vitest/utils": { "node_modules/@vitest/utils": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz",
"integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/pretty-format": "4.1.6", "@vitest/pretty-format": "4.1.7",
"convert-source-map": "^2.0.0", "convert-source-map": "^2.0.0",
"tinyrainbow": "^3.1.0" "tinyrainbow": "^3.1.0"
}, },
@@ -2120,9 +2084,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vue/test-utils": { "node_modules/@vue/test-utils": {
"version": "2.4.10", "version": "2.4.11",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.10.tgz", "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.11.tgz",
"integrity": "sha512-SmoZ5EA1kYiAFs9NkYdiFFQF+cSnUwnvlYEbY+DogWQZUiqOm/Y29eSbc5T6yi75SgSF9863SBeXniIEoPajCA==", "integrity": "sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2980,9 +2944,9 @@
} }
}, },
"node_modules/dompurify": { "node_modules/dompurify": {
"version": "3.4.3", "version": "3.4.11",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.3.tgz", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
"integrity": "sha512-VVwJidIJcp1hpg2OMXML3ZVRPYSZiq4aX7qBh83BSIpOaRDqI+qxhXjjIWnpzkOXhmp0L81lnoME1mnCc9H48A==", "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
"license": "(MPL-2.0 OR Apache-2.0)", "license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": { "optionalDependencies": {
"@types/trusted-types": "^2.0.7" "@types/trusted-types": "^2.0.7"
@@ -3140,15 +3104,15 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "10.3.0", "version": "10.4.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz",
"integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2", "@eslint-community/regexpp": "^4.12.2",
"@eslint/config-array": "^0.23.5", "@eslint/config-array": "^0.23.5",
"@eslint/config-helpers": "^0.5.5", "@eslint/config-helpers": "^0.6.0",
"@eslint/core": "^1.2.1", "@eslint/core": "^1.2.1",
"@eslint/plugin-kit": "^0.7.1", "@eslint/plugin-kit": "^0.7.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
@@ -3242,9 +3206,9 @@
} }
}, },
"node_modules/eslint-plugin-vue": { "node_modules/eslint-plugin-vue": {
"version": "10.9.1", "version": "10.9.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.2.tgz",
"integrity": "sha512-cHB0Tf4Duvzwecwd/AqWzZvF/QszE13BhjVUpVXWCy9AeMR5GjkAjP3i85vqgLgOuTmkHR1OJ5oMeqLHtuw8zg==", "integrity": "sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4236,9 +4200,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4259,9 +4220,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4282,9 +4240,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4305,9 +4260,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -4948,9 +4900,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.14", "version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -4967,7 +4919,7 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.12",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
@@ -4999,9 +4951,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.8.3", "version": "3.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@@ -5124,12 +5076,12 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/rolldown": { "node_modules/rolldown": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz",
"integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@oxc-project/types": "=0.130.0", "@oxc-project/types": "=0.132.0",
"@rolldown/pluginutils": "^1.0.0" "@rolldown/pluginutils": "^1.0.0"
}, },
"bin": { "bin": {
@@ -5139,21 +5091,21 @@
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.1", "@rolldown/binding-android-arm64": "1.0.2",
"@rolldown/binding-darwin-arm64": "1.0.1", "@rolldown/binding-darwin-arm64": "1.0.2",
"@rolldown/binding-darwin-x64": "1.0.1", "@rolldown/binding-darwin-x64": "1.0.2",
"@rolldown/binding-freebsd-x64": "1.0.1", "@rolldown/binding-freebsd-x64": "1.0.2",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.1", "@rolldown/binding-linux-arm-gnueabihf": "1.0.2",
"@rolldown/binding-linux-arm64-gnu": "1.0.1", "@rolldown/binding-linux-arm64-gnu": "1.0.2",
"@rolldown/binding-linux-arm64-musl": "1.0.1", "@rolldown/binding-linux-arm64-musl": "1.0.2",
"@rolldown/binding-linux-ppc64-gnu": "1.0.1", "@rolldown/binding-linux-ppc64-gnu": "1.0.2",
"@rolldown/binding-linux-s390x-gnu": "1.0.1", "@rolldown/binding-linux-s390x-gnu": "1.0.2",
"@rolldown/binding-linux-x64-gnu": "1.0.1", "@rolldown/binding-linux-x64-gnu": "1.0.2",
"@rolldown/binding-linux-x64-musl": "1.0.1", "@rolldown/binding-linux-x64-musl": "1.0.2",
"@rolldown/binding-openharmony-arm64": "1.0.1", "@rolldown/binding-openharmony-arm64": "1.0.2",
"@rolldown/binding-wasm32-wasi": "1.0.1", "@rolldown/binding-wasm32-wasi": "1.0.2",
"@rolldown/binding-win32-arm64-msvc": "1.0.1", "@rolldown/binding-win32-arm64-msvc": "1.0.2",
"@rolldown/binding-win32-x64-msvc": "1.0.1" "@rolldown/binding-win32-x64-msvc": "1.0.2"
} }
}, },
"node_modules/sass": { "node_modules/sass": {
@@ -6098,15 +6050,15 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "8.0.13", "version": "8.0.14",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
"integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"picomatch": "^4.0.4", "picomatch": "^4.0.4",
"postcss": "^8.5.14", "postcss": "^8.5.15",
"rolldown": "1.0.1", "rolldown": "1.0.2",
"tinyglobby": "^0.2.16" "tinyglobby": "^0.2.16"
}, },
"bin": { "bin": {
@@ -6281,19 +6233,19 @@
} }
}, },
"node_modules/vitest": { "node_modules/vitest": {
"version": "4.1.6", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz",
"integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==", "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vitest/expect": "4.1.6", "@vitest/expect": "4.1.7",
"@vitest/mocker": "4.1.6", "@vitest/mocker": "4.1.7",
"@vitest/pretty-format": "4.1.6", "@vitest/pretty-format": "4.1.7",
"@vitest/runner": "4.1.6", "@vitest/runner": "4.1.7",
"@vitest/snapshot": "4.1.6", "@vitest/snapshot": "4.1.7",
"@vitest/spy": "4.1.6", "@vitest/spy": "4.1.7",
"@vitest/utils": "4.1.6", "@vitest/utils": "4.1.7",
"es-module-lexer": "^2.0.0", "es-module-lexer": "^2.0.0",
"expect-type": "^1.3.0", "expect-type": "^1.3.0",
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
@@ -6321,12 +6273,12 @@
"@edge-runtime/vm": "*", "@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0", "@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.1.6", "@vitest/browser-playwright": "4.1.7",
"@vitest/browser-preview": "4.1.6", "@vitest/browser-preview": "4.1.7",
"@vitest/browser-webdriverio": "4.1.6", "@vitest/browser-webdriverio": "4.1.7",
"@vitest/coverage-istanbul": "4.1.6", "@vitest/coverage-istanbul": "4.1.7",
"@vitest/coverage-v8": "4.1.6", "@vitest/coverage-v8": "4.1.7",
"@vitest/ui": "4.1.6", "@vitest/ui": "4.1.7",
"happy-dom": "*", "happy-dom": "*",
"jsdom": "*", "jsdom": "*",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0" "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -40,6 +40,10 @@ abstract class CollectionMutableAbstract extends NodeMutableAbstract implements
// Copy all property values // Copy all property values
$this->properties->setLabel($value->getLabel()); $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; return $this;
} }
@@ -20,57 +20,53 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
*/ */
abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements CollectionPropertiesBaseInterface { abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements CollectionPropertiesBaseInterface {
public const JSON_TYPE = CollectionPropertiesBaseInterface::JSON_TYPE;
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function content(): CollectionContent { public function content(): array {
$content = $this->data[self::JSON_PROPERTY_CONTENTS] ?? null; $content = $this->data[self::PROPERTY_CONTENT] ?? null;
if ($content instanceof CollectionContent) { if (is_array($content)) {
return $content; return array_filter(array_map(function ($item) {
return $item instanceof CollectionContent ? $item : CollectionContent::tryFrom($item);
}, $content));
} else {
return [];
} }
if (is_string($content)) {
return CollectionContent::tryFrom($content) ?? CollectionContent::Event;
}
return CollectionContent::Event;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getLabel(): string { public function getLabel(): string {
return $this->data[self::JSON_PROPERTY_LABEL] ?? ''; return $this->data[self::PROPERTY_LABEL] ?? '';
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getDescription(): ?string { public function getDescription(): ?string {
return $this->data[self::JSON_PROPERTY_DESCRIPTION] ?? null; return $this->data[self::PROPERTY_DESCRIPTION] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getPriority(): ?int { public function getRank(): ?int {
return $this->data[self::JSON_PROPERTY_PRIORITY] ?? null; return $this->data[self::PROPERTY_RANK] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getVisibility(): ?bool { public function getVisibility(): ?bool {
return $this->data[self::JSON_PROPERTY_VISIBILITY] ?? null; return $this->data[self::PROPERTY_VISIBILITY] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getColor(): ?string { public function getColor(): ?string {
return $this->data[self::JSON_PROPERTY_COLOR] ?? null; return $this->data[self::PROPERTY_COLOR] ?? null;
} }
} }
@@ -14,15 +14,22 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface { interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'chrono:collection'; public const JSON_TYPE = 'chrono:collection';
public const JSON_PROPERTY_CONTENTS = 'content'; public const PROPERTY_CONTENT = 'content';
public const JSON_PROPERTY_LABEL = 'label'; public const PROPERTY_LABEL = 'label';
public const JSON_PROPERTY_DESCRIPTION = 'description'; public const PROPERTY_DESCRIPTION = 'description';
public const JSON_PROPERTY_PRIORITY = 'priority'; public const PROPERTY_RANK = 'rank';
public const JSON_PROPERTY_VISIBILITY = 'visibility'; public const PROPERTY_VISIBILITY = 'visibility';
public const JSON_PROPERTY_COLOR = 'color'; public const PROPERTY_COLOR = 'color';
public function content(): CollectionContent; /**
* 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;
/** /**
* Gets the human friendly name of this collection (e.g. Personal Calendar) * Gets the human friendly name of this collection (e.g. Personal Calendar)
@@ -39,11 +46,11 @@ interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface
public function getDescription(): ?string; public function getDescription(): ?string;
/** /**
* Gets the priority of this collection * Gets the rank of this collection
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
public function getPriority(): ?int; public function getRank(): ?int;
/** /**
* Gets the visibility of this collection * Gets the visibility of this collection
@@ -33,7 +33,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setLabel(string $value): static { public function setLabel(string $value): static {
$this->data[self::JSON_PROPERTY_LABEL] = $value; $this->data[self::PROPERTY_LABEL] = $value;
return $this; return $this;
} }
@@ -41,15 +41,15 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setDescription(?string $value): static { public function setDescription(?string $value): static {
$this->data[self::JSON_PROPERTY_DESCRIPTION] = $value; $this->data[self::PROPERTY_DESCRIPTION] = $value;
return $this; return $this;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function setPriority(?int $value): static { public function setRank(?int $value): static {
$this->data[self::JSON_PROPERTY_PRIORITY] = $value; $this->data[self::PROPERTY_RANK] = $value;
return $this; return $this;
} }
@@ -57,7 +57,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setVisibility(?bool $value): static { public function setVisibility(?bool $value): static {
$this->data[self::JSON_PROPERTY_VISIBILITY] = $value; $this->data[self::PROPERTY_VISIBILITY] = $value;
return $this; return $this;
} }
@@ -65,7 +65,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setColor(?string $value): static { public function setColor(?string $value): static {
$this->data[self::JSON_PROPERTY_COLOR] = $value; $this->data[self::PROPERTY_COLOR] = $value;
return $this; return $this;
} }
@@ -30,11 +30,11 @@ interface CollectionPropertiesMutableInterface extends CollectionPropertiesBaseI
public function setDescription(?string $value): static; public function setDescription(?string $value): static;
/** /**
* Sets the priority of this collection * Sets the rank of this collection
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
public function setPriority(?int $value): static; public function setRank(?int $value): static;
/** /**
* Sets the visibility of this collection * Sets the visibility of this collection
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace KTXF\Chrono\Entity; namespace KTXF\Chrono\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeBaseAbstract; use KTXF\Resource\Provider\Node\NodeBaseAbstract;
/** /**
@@ -20,7 +21,12 @@ use KTXF\Resource\Provider\Node\NodeBaseAbstract;
*/ */
abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface { abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface {
protected EntityPropertiesBaseAbstract $properties; 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]);
}
/** /**
* @inheritDoc * @inheritDoc
@@ -13,8 +13,6 @@ use KTXF\Resource\Provider\Node\NodeBaseInterface;
interface EntityBaseInterface extends NodeBaseInterface { interface EntityBaseInterface extends NodeBaseInterface {
public const JSON_TYPE = 'chrono.entity';
/** /**
* Gets the entity properties * Gets the entity properties
* *
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace KTXF\Chrono\Entity; namespace KTXF\Chrono\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeMutableAbstract; use KTXF\Resource\Provider\Node\NodeMutableAbstract;
use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface; use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
@@ -21,9 +22,12 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
*/ */
abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface { abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface {
public const JSON_TYPE = EntityMutableInterface::JSON_TYPE; protected string $type = 'chrono:entity';
protected EntityPropertiesMutableInterface $properties;
protected EntityPropertiesMutableAbstract $properties; 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]);
}
/** /**
* @inheritDoc * @inheritDoc
@@ -40,7 +44,7 @@ abstract class EntityMutableAbstract extends NodeMutableAbstract implements Enti
throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface'); throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface');
} }
$this->properties->setDataRaw($value->getDataRaw()); $this->properties = $value;
return $this; return $this;
} }
@@ -16,8 +16,6 @@ use KTXF\Resource\Provider\Node\NodeMutableInterface;
*/ */
interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface { interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface {
public const JSON_TYPE = EntityBaseInterface::JSON_TYPE;
/** /**
* Gets the entity properties (mutable) * Gets the entity properties (mutable)
* *
@@ -13,9 +13,10 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface { abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface {
public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE; /**
* @inheritDoc
*/
public function getDataRaw(): array|string|null { public function getDataRaw(): array|string|null {
return $this->data[self::JSON_PROPERTY_DATA] ?? null; return $this->data[self::PROPERTY_DATA] ?? null;
} }
} }
@@ -14,7 +14,7 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface { interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'chrono:entity'; public const JSON_TYPE = 'chrono:entity';
public const JSON_PROPERTY_DATA = 'data'; public const PROPERTY_DATA = 'data';
public function getDataRaw(): array|string|null; public function getDataRaw(): array|string|null;
@@ -24,7 +24,7 @@ abstract class EntityPropertiesMutableAbstract extends EntityPropertiesBaseAbstr
} }
public function setDataRaw(array|string|null $value): static { public function setDataRaw(array|string|null $value): static {
$this->data[self::JSON_PROPERTY_DATA] = $value; $this->data[self::PROPERTY_DATA] = $value;
return $this; return $this;
} }
} }
@@ -13,8 +13,6 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
interface EntityPropertiesMutableInterface extends EntityPropertiesBaseInterface, NodePropertiesMutableInterface { interface EntityPropertiesMutableInterface extends EntityPropertiesBaseInterface, NodePropertiesMutableInterface {
public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE;
public function setDataRaw(array|string|null $value): static; public function setDataRaw(array|string|null $value): static;
} }
@@ -7,9 +7,12 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity;
use DateTimeInterface; use DateTimeInterface;
use KTXF\Chrono\Entity\Property\EventCommonObject;
use KTXF\Chrono\Entity\Property\EventMutationCollection;
use KTXF\Chrono\Entity\Property\EventOccurrenceObject;
class EventObject extends EventCommonObject { class EventObject extends EventCommonObject {
@@ -0,0 +1,34 @@
<?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();
}
}
@@ -0,0 +1,13 @@
<?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');
}
}
@@ -0,0 +1,14 @@
<?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;
}
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventAvailabilityTypes: string { enum EventAvailabilityTypes: string {
case Free = 'free'; case Free = 'free';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use DateInterval; use DateInterval;
use DateTime; use DateTime;
@@ -33,7 +33,7 @@ class EventCommonObject extends JsonSerializableObject {
public EventSensitivityTypes|null $sensitivity = null; public EventSensitivityTypes|null $sensitivity = null;
public int|null $priority = null; public int|null $priority = null;
public string|null $color = null; public string|null $color = null;
public EventTagCollection $tags; public TagCollection $tags;
public EventOrganizerObject $organizer; public EventOrganizerObject $organizer;
public EventParticipantCollection $participants; public EventParticipantCollection $participants;
public EventNotificationCollection $notifications; public EventNotificationCollection $notifications;
@@ -44,7 +44,7 @@ class EventCommonObject extends JsonSerializableObject {
$this->locationsVirtual = new EventLocationVirtualCollection(); $this->locationsVirtual = new EventLocationVirtualCollection();
$this->notifications = new EventNotificationCollection(); $this->notifications = new EventNotificationCollection();
$this->organizer = new EventOrganizerObject(); $this->organizer = new EventOrganizerObject();
$this->tags = new EventTagCollection(); $this->tags = new TagCollection();
} }
} }
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection; use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableObject; use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection; use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableObject; use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection; use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use DateTime; use DateTime;
use DateTimeImmutable; use DateTimeImmutable;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventNotificationAnchorTypes: string { enum EventNotificationAnchorTypes: string {
case Start = 'start'; case Start = 'start';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection; use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use DateInterval; use DateInterval;
use DateTime; use DateTime;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventNotificationPatterns: string { enum EventNotificationPatterns: string {
case Absolute = 'absolute'; case Absolute = 'absolute';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventNotificationTypes: string { enum EventNotificationTypes: string {
case Visual = 'visual'; case Visual = 'visual';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use DateTime; use DateTime;
use DateTimeImmutable; use DateTimeImmutable;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventOccurrencePatternTypes: string { enum EventOccurrencePatternTypes: string {
case Absolute = 'absolute'; case Absolute = 'absolute';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventOccurrencePrecisionTypes: string { enum EventOccurrencePrecisionTypes: string {
case Yearly = 'yearly'; case Yearly = 'yearly';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableObject; use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection; use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableObject; use KTXF\Json\JsonSerializableObject;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventParticipantRealm: string { enum EventParticipantRealm: string {
case Internal = 'I'; case Internal = 'I';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
use KTXF\Json\JsonSerializableCollection; use KTXF\Json\JsonSerializableCollection;
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventParticipantRoleTypes: string { enum EventParticipantRoleTypes: string {
case Owner = 'owner'; case Owner = 'owner';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventParticipantStatusTypes: string { enum EventParticipantStatusTypes: string {
case None = 'none'; case None = 'none';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventParticipantTypes: string { enum EventParticipantTypes: string {
case Unknown = 'unknown'; case Unknown = 'unknown';
@@ -7,7 +7,7 @@ declare(strict_types=1);
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
*/ */
namespace KTXF\Chrono\Event; namespace KTXF\Chrono\Entity\Property;
enum EventSensitivityTypes: string { enum EventSensitivityTypes: string {
case Public = 'public'; case Public = 'public';
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
enum JournalStatusTypes: string {
case Draft = 'draft';
case Final = 'final';
case Cancelled = 'cancelled';
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace KTXF\Chrono\Entity\Property;
enum JournalVisibilityTypes: string {
case Public = 'public';
case Private = 'private';
case Confidential = 'confidential';
}
@@ -0,0 +1,13 @@
<?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');
}
}
@@ -0,0 +1,18 @@
<?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 = [];
}
@@ -0,0 +1,12 @@
<?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';
}
@@ -0,0 +1,13 @@
<?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');
}
}
@@ -0,0 +1,13 @@
<?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
@@ -0,0 +1,41 @@
<?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();
}
}
@@ -12,7 +12,7 @@ namespace KTXF\Chrono\Provider;
use KTXF\Resource\Provider\ResourceProviderBaseInterface; use KTXF\Resource\Provider\ResourceProviderBaseInterface;
/** /**
* Chrono Provider Base Interface * Provider Base Interface
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
@@ -12,11 +12,9 @@ namespace KTXF\Chrono\Provider;
use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface; use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
/** /**
* Chrono Provider Service Mutate Interface * 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 * @since 2025.05.01
* *
@@ -25,11 +23,8 @@ use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
* @method string serviceModify(string $tenantId, ?string $userId, ServiceMutableInterface $service) Modify a chrono service configuration * @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 * @method bool serviceDestroy(string $tenantId, ?string $userId, ServiceMutableInterface $service) Delete a chrono service configuration
*/ */
interface ProviderServiceMutateInterface extends ProviderBaseInterface, ResourceProviderServiceMutateInterface { interface ProviderServiceMutateInterface extends ResourceProviderServiceMutateInterface {
public const JSON_TYPE = ProviderBaseInterface::JSON_TYPE;
// Methods inherited from ResourceProviderServiceMutateInterface // Methods inherited from ResourceProviderServiceMutateInterface
// Implementations should return/accept ServiceMutableInterface instances
} }
@@ -10,36 +10,27 @@ declare(strict_types=1);
namespace KTXF\Chrono\Provider; namespace KTXF\Chrono\Provider;
use KTXF\Chrono\Service\ServiceBaseInterface; use KTXF\Chrono\Service\ServiceBaseInterface;
use KTXF\Chrono\Service\ServiceMutableInterface;
/** /**
* Chrono Provider Service Test Interface * Provider Service Test Interface
* *
* Optional interface for chrono providers that support testing service connections. * Optional interface for 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 * @since 2025.05.01
*/ */
interface ProviderServiceTestInterface extends ProviderBaseInterface { interface ProviderServiceTestInterface {
public const CAPABILITY_SERVICE_TEST = 'ServiceTest';
/** /**
* Test a service connection * 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 * @since 2025.05.01
* *
* @param ServiceBaseInterface $service Service to test (can be fresh/unsaved or existing) * @param ServiceBaseInterface|ServiceMutableInterface $service Service to test (can be fresh/unsaved or existing)
* @param array $options Provider-specific test options: * @param array $options Provider-specific test options:
* - 'timeout' => int (seconds, default: 10) * - 'timeout' => int (seconds, default: 10)
* - 'verify_ssl' => bool (default: true) * - 'verify_ssl' => bool (default: true)
@@ -48,10 +39,10 @@ interface ProviderServiceTestInterface extends ProviderBaseInterface {
* *
* @return array Test results in the format: * @return array Test results in the format:
* [ * [
* 'success' => bool, * 'disposition' => 'Success' | 'Failure' | 'Warning',
* 'message' => 'Connection successful' | 'Error message' * 'message' => 'Connection successful' | 'Error message',
* ] * ]
*/ */
public function serviceTest(ServiceBaseInterface $service, array $options = []): array; public function serviceTest(ServiceBaseInterface|ServiceMutableInterface $service, array $options = []): array;
} }
@@ -9,16 +9,21 @@ declare(strict_types=1);
namespace KTXF\Chrono\Service; namespace KTXF\Chrono\Service;
use Generator;
use KTXF\Chrono\Collection\CollectionBaseInterface; use KTXF\Chrono\Collection\CollectionBaseInterface;
use KTXF\Chrono\Entity\EntityBaseInterface;
use KTXF\Resource\Delta\Delta; use KTXF\Resource\Delta\Delta;
use KTXF\Resource\Filter\IFilter; use KTXF\Resource\Filter\IFilter;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
use KTXF\Resource\Provider\ResourceServiceBaseInterface; use KTXF\Resource\Provider\ResourceServiceBaseInterface;
use KTXF\Resource\Range\IRange; use KTXF\Resource\Range\IRange;
use KTXF\Resource\Range\RangeType; use KTXF\Resource\Range\RangeType;
use KTXF\Resource\Sort\ISort; use KTXF\Resource\Sort\ISort;
/** /**
* Chrono Service Base Interface * Service Base Interface
*
* Minimum interface for a service, providing read-only access to collections and entities.
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
@@ -70,7 +75,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* @param IFilter|null $filter Optional filter criteria * @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order * @param ISort|null $sort Optional sort order
* *
* @return array<string|int,CollectionBaseInterface> Collections indexed by ID * @return array<string|int,CollectionBaseInterface> Collections indexed by identifier
*/ */
public function collectionList(string|int $location, ?IFilter $filter = null, ?ISort $sort = null): array; public function collectionList(string|int $location, ?IFilter $filter = null, ?ISort $sort = null): array;
@@ -97,9 +102,9 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int ...$identifiers Collection IDs to check * @param string|int ...$identifiers Collection identifiers to check
* *
* @return array<string|int,bool> Map of ID => exists * @return array<string|int,bool> Map of identifier => exists
*/ */
public function collectionExtant(string|int $location, string|int ...$identifiers): array; public function collectionExtant(string|int $location, string|int ...$identifiers): array;
@@ -108,29 +113,44 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $identifier Collection ID * @param string|int $identifier Collection identifier
* *
* @return CollectionBaseInterface|null Collection or null if not found * @return CollectionBaseInterface|null Collection or null if not found
*/ */
public function collectionFetch(string|int $identifier): ?CollectionBaseInterface; public function collectionFetch(string|int $identifier): ?CollectionBaseInterface;
/** /**
* Lists messages in a collection * Lists entities in a collection
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param IFilter|null $filter Optional filter criteria * @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order * @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination * @param IRange|null $range Optional pagination
* @param array|null $properties Optional message properties to fetch * @param array|null $properties Optional entity properties to fetch
* *
* @return array<string|int,EntityBaseInterface> Messages indexed by ID * @return array<string,EntityBaseInterface> Entities indexed by Urn
*/ */
public function entityList(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array; public function entityListBulk(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array;
/** /**
* Creates a filter builder for messages * 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
* *
* @since 2025.05.01 * @since 2025.05.01
* *
@@ -139,7 +159,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListFilter(): IFilter; public function entityListFilter(): IFilter;
/** /**
* Creates a sort builder for messages * Creates a sort builder for entities
* *
* @since 2025.05.01 * @since 2025.05.01
* *
@@ -148,7 +168,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListSort(): ISort; public function entityListSort(): ISort;
/** /**
* Creates a range builder for messages * Creates a range builder for entities
* *
* @since 2025.05.01 * @since 2025.05.01
* *
@@ -159,40 +179,49 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListRange(RangeType $type): IRange; public function entityListRange(RangeType $type): IRange;
/** /**
* Gets incremental changes since last sync * Gets incremental changes since last signature
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param string $signature Sync token from previous sync * @param string $signature Token from previous delta
* @param string $detail Detail level: 'ids', 'minimal', 'full'
* *
* @return array ['signature' => string, 'added' => array, 'modified' => array, 'removed' => array] * @return array ['signature' => string, 'added' => array, 'modified' => array, 'removed' => array]
*/ */
public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta; public function entityDelta(string|int $collection, string $signature): Delta;
/** /**
* Checks if messages exist * Checks if entities exist
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param string|int ...$identifiers Message IDs to check * @param string|int ...$identifiers Entity identifiers to check
* *
* @return array<string|int,bool> Map of ID => exists * @return array<string|int,bool> Map of identifier => exists
*/ */
public function entityExtant(string|int $collection, string|int ...$identifiers): array; public function entityExtant(string|int $collection, string|int ...$identifiers): array;
/** /**
* Fetches one or more entities * Fetches one or more entities as an array
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param EntityIdentifierInterface ...$identifiers Entity identifiers to fetch
* @param string|int ...$identifiers Message IDs to fetch
* *
* @return array<string|int,EntityBaseInterface> Messages indexed by ID * @return array<string,EntityBaseInterface> Entities indexed by Urn
*/ */
public function entityFetch(string|int $collection, string|int ...$identifiers): array; 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;
} }
@@ -11,13 +11,17 @@ namespace KTXF\Chrono\Service;
use KTXF\Chrono\Collection\CollectionBaseInterface; use KTXF\Chrono\Collection\CollectionBaseInterface;
use KTXF\Chrono\Collection\CollectionMutableInterface; use KTXF\Chrono\Collection\CollectionMutableInterface;
use KTXF\Chrono\Collection\CollectionPropertiesBaseInterface;
use KTXF\Resource\Identifier\CollectionIdentifierInterface;
/** /**
* Chrono Service Collection Mutable Interface * Service Collection Mutable Interface
*
* Optional interface for services that support collection CRUD operations.
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
interface ServiceCollectionMutableInterface extends ServiceBaseInterface { interface ServiceCollectionMutableInterface {
public const CAPABILITY_COLLECTION_CREATE = 'CollectionCreate'; public const CAPABILITY_COLLECTION_CREATE = 'CollectionCreate';
public const CAPABILITY_COLLECTION_UPDATE = 'CollectionUpdate'; public const CAPABILITY_COLLECTION_UPDATE = 'CollectionUpdate';
@@ -37,37 +41,36 @@ interface ServiceCollectionMutableInterface extends ServiceBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int|null $location Parent collection ID (null for root) * @param CollectionIdentifierInterface|null $target Target collection identifier (parent)
* @param CollectionMutableInterface $collection Collection to create * @param CollectionPropertiesBaseInterface $properties Collection properties
* @param array $options Protocol-specific options * @param array $options Protocol-specific options
* *
* @return CollectionBaseInterface Created collection with assigned ID * @return CollectionBaseInterface Created collection with assigned ID
*/ */
public function collectionCreate(string|int|null $location, CollectionMutableInterface $collection, array $options = []): CollectionBaseInterface; public function collectionCreate(CollectionIdentifierInterface|null $target, CollectionPropertiesBaseInterface $properties, array $options = []): CollectionBaseInterface;
/** /**
* Updates an existing collection * Updates an existing collection
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $identifier Collection ID * @param CollectionIdentifierInterface $target Target collection identifier
* @param CollectionMutableInterface $collection Updated collection data * @param CollectionPropertiesBaseInterface $properties Updated collection data
* *
* @return CollectionBaseInterface Updated collection * @return CollectionBaseInterface Updated collection
*/ */
public function collectionUpdate(string|int $identifier, CollectionMutableInterface $collection): CollectionBaseInterface; public function collectionUpdate(CollectionIdentifierInterface $target, CollectionPropertiesBaseInterface $properties): CollectionBaseInterface;
/** /**
* Deletes a collection * Deletes a collection
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $identifier Collection ID * @param CollectionIdentifierInterface $target Target collection identifier
* @param bool $force Force deletion even if not empty * @param bool $force Force deletion even if not empty
* @param bool $recursive Recursively delete contents
* *
* @return bool True if deleted * @return CollectionBaseInterface|true Collection object on soft delete, true on hard delete
*/ */
public function collectionDelete(string|int $identifier, bool $force = false, bool $recursive = false): bool; public function collectionDelete(CollectionIdentifierInterface $target, bool $force = false): CollectionBaseInterface | true;
} }
@@ -12,15 +12,12 @@ namespace KTXF\Chrono\Service;
use KTXF\Resource\Provider\ResourceServiceConfigureInterface; use KTXF\Resource\Provider\ResourceServiceConfigureInterface;
/** /**
* Chrono Service Configurable Interface * 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 * @since 2025.05.01
*/ */
interface ServiceConfigurableInterface extends ServiceMutableInterface, ResourceServiceConfigureInterface { interface ServiceConfigurableInterface extends ServiceMutableInterface, ResourceServiceConfigureInterface {
public const JSON_TYPE = ServiceBaseInterface::JSON_TYPE;
} }
@@ -11,19 +11,21 @@ namespace KTXF\Chrono\Service;
use KTXF\Chrono\Entity\EntityBaseInterface; use KTXF\Chrono\Entity\EntityBaseInterface;
use KTXF\Chrono\Entity\EntityMutableInterface; use KTXF\Chrono\Entity\EntityMutableInterface;
use KTXF\Chrono\Entity\EntityPropertiesMutableInterface;
use KTXF\Resource\Identifier\CollectionIdentifierInterface;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
/** /**
* Chrono Service Entity Mutable Interface * 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 * @since 2025.05.01
*/ */
interface ServiceEntityMutableInterface extends ServiceBaseInterface { interface ServiceEntityMutableInterface {
public const CAPABILITY_ENTITY_CREATE = 'EntityCreate'; public const CAPABILITY_ENTITY_CREATE = 'EntityCreate';
public const CAPABILITY_ENTITY_UPDATE = 'EntityUpdate'; public const CAPABILITY_ENTITY_MODIFY = 'EntityModify';
public const CAPABILITY_ENTITY_DELETE = 'EntityDelete'; public const CAPABILITY_ENTITY_DELETE = 'EntityDelete';
public const CAPABILITY_ENTITY_COPY = 'EntityCopy'; public const CAPABILITY_ENTITY_COPY = 'EntityCopy';
public const CAPABILITY_ENTITY_MOVE = 'EntityMove'; public const CAPABILITY_ENTITY_MOVE = 'EntityMove';
@@ -42,36 +44,71 @@ interface ServiceEntityMutableInterface extends ServiceBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection collection identifier * @param CollectionIdentifierInterface $target Target collection identifier
* @param EntityMutableInterface $entity Entity data * @param EntityPropertiesMutableInterface $properties Entity properties
* @param array $options additional options * @param array $options Additional options
* *
* @return EntityBaseInterface Created entity * @return EntityBaseInterface Created entity
*/ */
public function entityCreate(string|int $collection, EntityMutableInterface $entity, array $options = []): EntityBaseInterface; public function entityCreate(CollectionIdentifierInterface $target, EntityPropertiesMutableInterface $properties, array $options = []): EntityBaseInterface;
/** /**
* Modifies an existing entity * Modifies an existing entity
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection identifier * @param EntityIdentifierInterface $target Target entity identifier
* @param string|int $identifier Entity identifier * @param EntityPropertiesMutableInterface $properties Entity properties to update
* @param EntityMutableInterface $entity Entity data
* *
* @return EntityBaseInterface Modified entity * @return EntityBaseInterface Modified entity
*/ */
public function entityUpdate(string|int $collection, string|int $identifier, EntityMutableInterface $entity): EntityBaseInterface; public function entityModify(EntityIdentifierInterface $target, EntityPropertiesMutableInterface $properties): EntityBaseInterface;
/** /**
* Deletes an existing entity in the specified collection * 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
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection identifier * @param CollectionIdentifierInterface $target Target collection identifier
* @param string|int $identifier Entity identifier to delete * @param EntityIdentifierInterface ...$sources Source entities to move
* *
* @return EntityBaseInterface Deleted entity * @return array<string, array{
* disposition: 'moved'|'error',
* destination: ?CollectionIdentifierInterface,
* mutation: EntityIdentifierInterface
* }> Results keyed by source entity identifier
*/ */
public function entityDelete(string|int $collection, string|int $identifier): EntityBaseInterface; 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;
} }
@@ -12,11 +12,10 @@ namespace KTXF\Chrono\Service;
/** /**
* Chrono Service Mutable Interface * 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 * @since 2025.05.01
*/ */
interface ServiceMutableInterface extends ServiceBaseInterface { interface ServiceMutableInterface {
} }
+10 -10
View File
@@ -14,16 +14,16 @@ use DateTimeImmutable;
interface INodeBase extends \JsonSerializable { interface INodeBase extends \JsonSerializable {
public const JSON_TYPE = 'files.node'; public const JSON_TYPE = 'files.node';
public const JSON_PROPERTY_TYPE = '@type'; public const PROPERTY_TYPE = '@type';
public const JSON_PROPERTY_IN = 'in'; public const PROPERTY_IN = 'in';
public const JSON_PROPERTY_ID = 'id'; public const PROPERTY_ID = 'id';
public const JSON_PROPERTY_CREATED_ON = 'createdOn'; public const PROPERTY_CREATED_ON = 'createdOn';
public const JSON_PROPERTY_CREATED_BY = 'createdBy'; public const PROPERTY_CREATED_BY = 'createdBy';
public const JSON_PROPERTY_MODIFIED_ON = 'modifiedOn'; public const PROPERTY_MODIFIED_ON = 'modifiedOn';
public const JSON_PROPERTY_MODIFIED_BY = 'modifiedBy'; public const PROPERTY_MODIFIED_BY = 'modifiedBy';
public const JSON_PROPERTY_OWNER = 'owner'; public const PROPERTY_OWNER = 'owner';
public const JSON_PROPERTY_SIGNATURE = 'signature'; public const PROPERTY_SIGNATURE = 'signature';
public const JSON_PROPERTY_LABEL = 'label'; public const PROPERTY_LABEL = 'label';
/** /**
* Unique identifier of the parent node (folder) this node belongs to * 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 { interface INodeEntityBase extends INodeBase {
public const JSON_TYPE = 'files.entity'; public const JSON_TYPE = 'files.entity';
public const JSON_PROPERTY_SIZE = 'size'; public const PROPERTY_SIZE = 'size';
public const JSON_PROPERTY_MIME = 'mime'; public const PROPERTY_MIME = 'mime';
public const JSON_PROPERTY_FORMAT = 'format'; public const PROPERTY_FORMAT = 'format';
public const JSON_PROPERTY_ENCODING = 'encoding'; public const PROPERTY_ENCODING = 'encoding';
/** /**
* File size in bytes * File size in bytes
@@ -14,15 +14,15 @@ use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeBaseAbstract; use KTXF\Resource\Provider\Node\NodeBaseAbstract;
/** /**
* Abstract Mail Entity Base Class * Abstract Entity Base Class
* *
* Provides common implementation for mail entities * Provides common implementation for entities
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface { abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface {
protected string $type = 'mail.entity'; protected string $type = 'mail:entity';
protected MessagePropertiesBaseInterface $properties; protected MessagePropertiesBaseInterface $properties;
protected function nodeIdentifier(): EntityIdentifier { protected function nodeIdentifier(): EntityIdentifier {
@@ -15,15 +15,15 @@ use KTXF\Resource\Provider\Node\NodeMutableAbstract;
use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface; use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
/** /**
* Abstract Mail Entity Mutable Class * Abstract Entity Mutable Class
* *
* Provides common implementation for mutable mail entities * Provides common implementation for mutable entities
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface { abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface {
protected string $type = 'mail.entity'; protected string $type = 'mail:entity';
protected MessagePropertiesMutableInterface $properties; protected MessagePropertiesMutableInterface $properties;
protected function nodeIdentifier(): EntityIdentifier { protected function nodeIdentifier(): EntityIdentifier {
+20 -13
View File
@@ -25,16 +25,6 @@ class Address implements AddressInterface {
private ?string $name = null, 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 * Creates an Address from a formatted string
* *
@@ -66,17 +56,34 @@ class Address implements AddressInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param array $data Array with 'address' and optional 'name' keys * @param array<address: string, label: string|null> $data
* *
* @return self * @return self
*/ */
public static function fromArray(array $data): self { public static function fromArray(array $data): self {
return new self( return new self(
$data[self::JSON_PROPERTY_ADDRESS] ?? $data['address'] ?? '', $data[self::PROPERTY_ADDRESS] ?? $data['address'] ?? '',
$data[self::JSON_PROPERTY_LABEL] ?? $data['name'] ?? null, $data[self::PROPERTY_LABEL] ?? $data['label'] ?? 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 * @inheritDoc
*/ */
+9 -2
View File
@@ -20,8 +20,15 @@ use KTXF\Json\JsonSerializable;
*/ */
interface AddressInterface extends JsonSerializable { interface AddressInterface extends JsonSerializable {
public const JSON_PROPERTY_ADDRESS = 'address'; public const PROPERTY_ADDRESS = 'address';
public const JSON_PROPERTY_LABEL = 'label'; public const PROPERTY_LABEL = 'label';
/*
* Converts the Address to an array representation
*
* @return array<address: string, label: string|null>
*/
public function toArray(): array;
/** /**
* Gets the email address * Gets the email address
-194
View File
@@ -1,194 +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;
/**
* 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);
}
}
@@ -1,93 +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;
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
@@ -0,0 +1,37 @@
<?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 * Constructor
* *
* @param array &$data Reference to data array * @param array|null &$data Reference to data array
*/ */
public function __construct(array &$data = null) { public function __construct(array|null &$data = null) {
if ($data === null) { if ($data === null) {
$data = []; $data = [];
} }
@@ -52,49 +52,70 @@ abstract class MessagePartBaseAbstract implements MessagePartInterface {
* @inheritDoc * @inheritDoc
*/ */
public function getId(): ?string { public function getId(): ?string {
return $this->data['partId'] ?? null; return $this->data[MessagePartInterface::PROPERTY_PART_ID] ?? null;
}
/**
* @inheritDoc
*/
public function getSize(): ?int {
return $this->data[MessagePartInterface::PROPERTY_SIZE] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getType(): ?string { public function getType(): ?string {
return $this->data['type'] ?? null; return $this->data[MessagePartInterface::PROPERTY_TYPE] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getDisposition(): ?string { public function getDisposition(): ?string {
return $this->data['disposition'] ?? null; return $this->data[MessagePartInterface::PROPERTY_DISPOSITION] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getName(): ?string { public function getName(): ?string {
return $this->data['name'] ?? null; return $this->data[MessagePartInterface::PROPERTY_NAME] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getCharset(): ?string { public function getCharset(): ?string {
return $this->data['charset'] ?? null; return $this->data[MessagePartInterface::PROPERTY_CHARSET] ?? null;
}
/**
* @inheritDoc
*/
public function getContentId(): ?string {
return $this->data[MessagePartInterface::PROPERTY_CONTENT_ID] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getLanguage(): ?string { public function getLanguage(): ?string {
return $this->data['language'] ?? null; return $this->data[MessagePartInterface::PROPERTY_LANGUAGE] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getLocation(): ?string { public function getLocation(): ?string {
return $this->data['location'] ?? null; return $this->data[MessagePartInterface::PROPERTY_LOCATION] ?? null;
}
/**
* @inheritDoc
*/
public function getContent(): ?string {
return $this->data[MessagePartInterface::PROPERTY_CONTENT] ?? null;
} }
/** /**
@@ -111,9 +132,9 @@ abstract class MessagePartBaseAbstract implements MessagePartInterface {
$result = $this->data; $result = $this->data;
if (!empty($this->parts)) { if (!empty($this->parts)) {
$result['subParts'] = []; $result[MessagePartInterface::PROPERTY_SUB_PARTS] = [];
foreach ($this->parts as $part) { foreach ($this->parts as $part) {
$result['subParts'][] = $part->jsonSerialize(); $result[MessagePartInterface::PROPERTY_SUB_PARTS][] = $part->jsonSerialize();
} }
} }
@@ -20,6 +20,19 @@ use KTXF\Json\JsonSerializable;
*/ */
interface MessagePartInterface extends 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 * Gets the blob identifier
* *
@@ -38,6 +51,15 @@ interface MessagePartInterface extends JsonSerializable {
*/ */
public function getId(): ?string; 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 * Gets the MIME type
* *
@@ -56,6 +78,15 @@ interface MessagePartInterface extends JsonSerializable {
*/ */
public function getDisposition(): ?string; 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 * Gets the part name
* *
@@ -92,6 +123,15 @@ interface MessagePartInterface extends JsonSerializable {
*/ */
public function getLocation(): ?string; public function getLocation(): ?string;
/**
* Gets the decoded part content
*
* @since 2026.06.15
*
* @return string|null
*/
public function getContent(): ?string;
/** /**
* Gets the sub-parts * Gets the sub-parts
* *
@@ -46,6 +46,16 @@ abstract class MessagePartMutableAbstract extends MessagePartBaseAbstract {
return $this; 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 * Sets the MIME type
* *
@@ -102,6 +112,16 @@ abstract class MessagePartMutableAbstract extends MessagePartBaseAbstract {
return $this; 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 * Sets the language
* *
@@ -130,6 +150,16 @@ abstract class MessagePartMutableAbstract extends MessagePartBaseAbstract {
return $this; 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 * Sets the sub-parts
* *
@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace KTXF\Mail\Object; namespace KTXF\Mail\Object;
use DateTimeImmutable; use DateTimeImmutable;
use Exception;
use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract; use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
/** /**
@@ -21,7 +22,110 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
*/ */
abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract implements MessagePropertiesBaseInterface { abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract implements MessagePropertiesBaseInterface {
protected string $type = 'mail.message'; 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;
}
/** /**
* @inheritDoc * @inheritDoc
@@ -76,56 +180,56 @@ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract
* @inheritDoc * @inheritDoc
*/ */
public function getReceived(): ?DateTimeImmutable { public function getReceived(): ?DateTimeImmutable {
return $this->data[static::PROPERTY_RECEIVED] ?? null; return $this->materializeDate($this->data[static::PROPERTY_RECEIVED] ?? null);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getSent(): ?DateTimeImmutable { public function getSent(): ?DateTimeImmutable {
return $this->data[static::PROPERTY_SENT] ?? null; return $this->materializeDate($this->data[static::PROPERTY_SENT] ?? null);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getSender(): ?AddressInterface { public function getSender(): ?AddressInterface {
return $this->data[static::PROPERTY_SENDER] ?? null; return $this->materializeAddress($this->data[static::PROPERTY_SENDER] ?? null);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getFrom(): ?AddressInterface { public function getFrom(): ?AddressInterface {
return $this->data[static::PROPERTY_FROM] ?? null; return $this->materializeAddress($this->data[static::PROPERTY_FROM] ?? null);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getReplyTo(): array { public function getReplyTo(): array {
return $this->data[static::PROPERTY_REPLY_TO] ?? []; return $this->materializeAddresses($this->data[static::PROPERTY_REPLY_TO] ?? []);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getTo(): array { public function getTo(): array {
return $this->data[static::PROPERTY_TO] ?? []; return $this->materializeAddresses($this->data[static::PROPERTY_TO] ?? []);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getCc(): array { public function getCc(): array {
return $this->data[static::PROPERTY_CC] ?? []; return $this->materializeAddresses($this->data[static::PROPERTY_CC] ?? []);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getBcc(): array { public function getBcc(): array {
return $this->data[static::PROPERTY_BCC] ?? []; return $this->materializeAddresses($this->data[static::PROPERTY_BCC] ?? []);
} }
/** /**
@@ -139,36 +243,38 @@ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract
* @inheritDoc * @inheritDoc
*/ */
public function getBody(): ?MessagePartInterface { public function getBody(): ?MessagePartInterface {
return $this->data[static::PROPERTY_BODY] ?? null; return $this->materializePart($this->data[static::PROPERTY_BODY] ?? null);
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function hasBody(): bool { public function hasBody(): bool {
return ($this->data[static::PROPERTY_BODY_TEXT_PLAIN] !== null && $this->data[static::PROPERTY_BODY_TEXT_PLAIN] !== '') $body = $this->getBody();
|| ($this->data[static::PROPERTY_BODY_TEXT_HTML] !== null && $this->data[static::PROPERTY_BODY_TEXT_HTML] !== '');
return $this->extractBodyContent($body, 'text/plain') !== null
|| $this->extractBodyContent($body, 'text/html') !== null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getBodyTextPlain(): ?string { public function getBodyTextPlain(): ?string {
return $this->data[static::PROPERTY_BODY_TEXT_PLAIN] ?? null; return $this->extractBodyContent($this->getBody(), 'text/plain');
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getBodyTextHtml(): ?string { public function getBodyTextHtml(): ?string {
return $this->data[static::PROPERTY_BODY_TEXT_HTML] ?? null; return $this->extractBodyContent($this->getBody(), 'text/html');
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getAttachments(): array { public function getAttachments(): array {
return $this->data[static::PROPERTY_ATTACHMENTS] ?? []; return $this->materializeParts($this->data[static::PROPERTY_ATTACHMENTS] ?? []);
} }
/** /**
@@ -176,7 +282,7 @@ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract
*/ */
public function getFlags(): array { public function getFlags(): array {
return $this->data[static::PROPERTY_FLAGS] ?? [ return $this->data[static::PROPERTY_FLAGS] ?? [
'read' => false, 'seen' => false,
'starred' => false, 'starred' => false,
'important' => false, 'important' => false,
'answered' => false, 'answered' => false,
@@ -34,8 +34,6 @@ interface MessagePropertiesBaseInterface extends NodePropertiesBaseInterface {
public const PROPERTY_BCC = 'bcc'; public const PROPERTY_BCC = 'bcc';
public const PROPERTY_SUBJECT = 'subject'; public const PROPERTY_SUBJECT = 'subject';
public const PROPERTY_BODY = 'body'; 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_ATTACHMENTS = 'attachments';
public const PROPERTY_FLAGS = 'flags'; public const PROPERTY_FLAGS = 'flags';
public const PROPERTY_TAGS = 'tags'; public const PROPERTY_TAGS = 'tags';
@@ -217,7 +215,7 @@ interface MessagePropertiesBaseInterface extends NodePropertiesBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @return array<int,AttachmentInterface> * @return array<int,MessagePartInterface>
*/ */
public function getAttachments(): array; public function getAttachments(): array;
@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace KTXF\Mail\Object; namespace KTXF\Mail\Object;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeInterface;
/** /**
* Abstract Message Properties Mutable Class * Abstract Message Properties Mutable Class
@@ -22,6 +23,90 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
protected string $type = 'mail.message'; 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 * @inheritDoc
*/ */
@@ -91,16 +176,16 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function setReceived(?DateTimeImmutable $value): static { public function setReceived(?DateTimeInterface $value): static {
$this->data[static::PROPERTY_RECEIVED] = $value; $this->data[static::PROPERTY_RECEIVED] = $this->normalizeDate($value);
return $this; return $this;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function setSent(DateTimeImmutable $value): static { public function setSent(?DateTimeInterface $value): static {
$this->data[static::PROPERTY_SENT] = $value; $this->data[static::PROPERTY_SENT] = $this->normalizeDate($value);
return $this; return $this;
} }
@@ -108,7 +193,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setSender(?AddressInterface $value): static { public function setSender(?AddressInterface $value): static {
$this->data[static::PROPERTY_SENDER] = $value; $this->data[static::PROPERTY_SENDER] = $value?->toArray();
return $this; return $this;
} }
@@ -116,7 +201,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setFrom(AddressInterface $value): static { public function setFrom(AddressInterface $value): static {
$this->data[static::PROPERTY_FROM] = $value; $this->data[static::PROPERTY_FROM] = $value->toArray();
return $this; return $this;
} }
@@ -124,7 +209,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setReplyTo(AddressInterface ...$value): static { public function setReplyTo(AddressInterface ...$value): static {
$this->data[static::PROPERTY_REPLY_TO] = $value; $this->data[static::PROPERTY_REPLY_TO] = $this->normalizeAddresses($value);
return $this; return $this;
} }
@@ -132,7 +217,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setTo(AddressInterface ...$value): static { public function setTo(AddressInterface ...$value): static {
$this->data[static::PROPERTY_TO] = $value; $this->data[static::PROPERTY_TO] = $this->normalizeAddresses($value);
return $this; return $this;
} }
@@ -140,7 +225,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setCc(AddressInterface ...$value): static { public function setCc(AddressInterface ...$value): static {
$this->data[static::PROPERTY_CC] = $value; $this->data[static::PROPERTY_CC] = $this->normalizeAddresses($value);
return $this; return $this;
} }
@@ -148,7 +233,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setBcc(AddressInterface ...$value): static { public function setBcc(AddressInterface ...$value): static {
$this->data[static::PROPERTY_BCC] = $value; $this->data[static::PROPERTY_BCC] = $this->normalizeAddresses($value);
return $this; return $this;
} }
@@ -164,7 +249,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setBody(?MessagePartInterface $value): static { public function setBody(?MessagePartInterface $value): static {
$this->data[static::PROPERTY_BODY] = $value; $this->data[static::PROPERTY_BODY] = $value?->jsonSerialize();
return $this; return $this;
} }
@@ -172,31 +257,29 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
* @inheritDoc * @inheritDoc
*/ */
public function setBodyTextPlain(?string $value): static { public function setBodyTextPlain(?string $value): static {
$this->data[static::PROPERTY_BODY_TEXT_PLAIN] = $value; return $this->updateBodyContent('text/plain', $value);
return $this;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function setBodyTextHtml(?string $value): static { public function setBodyTextHtml(?string $value): static {
$this->data[static::PROPERTY_BODY_TEXT_HTML] = $value; return $this->updateBodyContent('text/html', $value);
}
/**
* @inheritDoc
*/
public function setAttachments(\KTXF\Mail\Object\MessagePartInterface ...$value): static {
$this->data[static::PROPERTY_ATTACHMENTS] = $this->normalizeParts($value);
return $this; return $this;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function setAttachments(AttachmentInterface ...$value): static { public function addAttachment(\KTXF\Mail\Object\MessagePartInterface $value): static {
$this->data[static::PROPERTY_ATTACHMENTS] = $value; $this->data[static::PROPERTY_ATTACHMENTS][] = $value->jsonSerialize();
return $this;
}
/**
* @inheritDoc
*/
public function addAttachment(AttachmentInterface $value): static {
$this->data[static::PROPERTY_ATTACHMENTS][] = $value;
return $this; return $this;
} }
@@ -212,7 +295,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
public function setFlags(array $value): static { public function setFlags(array $value): static {
if (!isset($this->data[static::PROPERTY_FLAGS])) { if (!isset($this->data[static::PROPERTY_FLAGS])) {
$this->data[static::PROPERTY_FLAGS] = [ $this->data[static::PROPERTY_FLAGS] = [
'read' => false, 'seen' => false,
'starred' => false, 'starred' => false,
'important' => false, 'important' => false,
'answered' => false, 'answered' => false,
@@ -232,7 +315,7 @@ abstract class MessagePropertiesMutableAbstract extends MessagePropertiesBaseAbs
public function setFlag(string $name, bool $value): static { public function setFlag(string $name, bool $value): static {
if (!isset($this->data[static::PROPERTY_FLAGS])) { if (!isset($this->data[static::PROPERTY_FLAGS])) {
$this->data[static::PROPERTY_FLAGS] = [ $this->data[static::PROPERTY_FLAGS] = [
'read' => false, 'seen' => false,
'starred' => false, 'starred' => false,
'important' => false, 'important' => false,
'answered' => false, 'answered' => false,
@@ -223,22 +223,22 @@ interface MessagePropertiesMutableInterface extends MessagePropertiesBaseInterfa
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param AttachmentInterface ...$value * @param MessagePartInterface ...$value
* *
* @return self * @return self
*/ */
public function setAttachments(AttachmentInterface ...$value): static; public function setAttachments(MessagePartInterface ...$value): static;
/** /**
* Adds an attachment * Adds an attachment
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param AttachmentInterface $value * @param MessagePartInterface $value
* *
* @return self * @return self
*/ */
public function addAttachment(AttachmentInterface $value): static; public function addAttachment(MessagePartInterface $value): static;
/** /**
* Sets message flags * Sets message flags
@@ -13,15 +13,15 @@ use KTXF\Mail\Service\ServiceBaseInterface;
use KTXF\Resource\Provider\ResourceProviderBaseInterface; use KTXF\Resource\Provider\ResourceProviderBaseInterface;
/** /**
* Mail Provider Base Interface * Provider Base Interface
* *
* Core interface for mail providers with context-aware service discovery. * Core interface for providers with context-aware service discovery.
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
interface ProviderBaseInterface extends ResourceProviderBaseInterface{ 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 * Finds a service that handles a specific email address
@@ -12,11 +12,11 @@ namespace KTXF\Mail\Provider;
use KTXF\Resource\Provider\ResourceServiceLocationInterface; use KTXF\Resource\Provider\ResourceServiceLocationInterface;
/** /**
* Mail Provider Autodiscovery Interface * Provider Autodiscovery Interface
* *
* Optional interface for mail providers that support automatic service discovery * Optional interface for providers that support automatic service discovery
* from email addresses or domains. Providers implementing this interface can * from email addresses or domains. Providers implementing this interface can
* discover mail service configurations using various methods specific to their * discover service configurations using various methods specific to their
* protocol or provider type. * protocol or provider type.
* *
* Examples: * Examples:
@@ -28,6 +28,8 @@ use KTXF\Resource\Provider\ResourceServiceLocationInterface;
*/ */
interface ProviderServiceDiscoverInterface { interface ProviderServiceDiscoverInterface {
public const CAPABILITY_SERVICE_DISCOVER = 'ServiceDiscover';
/** /**
* Attempts to discover service configuration using provider-specific methods. * Attempts to discover service configuration using provider-specific methods.
* *
@@ -12,11 +12,9 @@ namespace KTXF\Mail\Provider;
use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface; use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
/** /**
* Mail Provider Service Mutate Interface * 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 * @since 2025.05.01
* *
@@ -28,6 +26,5 @@ use KTXF\Resource\Provider\ResourceProviderServiceMutateInterface;
interface ProviderServiceMutateInterface extends ResourceProviderServiceMutateInterface { interface ProviderServiceMutateInterface extends ResourceProviderServiceMutateInterface {
// Methods inherited from ResourceProviderServiceMutateInterface // Methods inherited from ResourceProviderServiceMutateInterface
// Implementations should return/accept ServiceMutableInterface instances
} }
@@ -13,30 +13,20 @@ use KTXF\Mail\Service\ServiceBaseInterface;
use KTXF\Mail\Service\ServiceMutableInterface; use KTXF\Mail\Service\ServiceMutableInterface;
/** /**
* Mail Provider Service Test Interface * Provider Service Test Interface
* *
* Optional interface for mail providers that support testing service connections. * Optional interface for 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 * @since 2025.05.01
*/ */
interface ProviderServiceTestInterface { interface ProviderServiceTestInterface {
public const CAPABILITY_SERVICE_TEST = 'ServiceTest';
/** /**
* Test a service connection * 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 * @since 2025.05.01
* *
@@ -49,28 +39,8 @@ interface ProviderServiceTestInterface {
* *
* @return array Test results in the format: * @return array Test results in the format:
* [ * [
* 'success' => bool, * 'disposition' => 'Success' | 'Failure' | 'Warning',
* 'message' => 'Connection successful' | 'Error message', * '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; public function serviceTest(ServiceBaseInterface|ServiceMutableInterface $service, array $options = []): array;
@@ -11,19 +11,21 @@ namespace KTXF\Mail\Service;
use Generator; use Generator;
use KTXF\Mail\Collection\CollectionBaseInterface; use KTXF\Mail\Collection\CollectionBaseInterface;
use KTXF\Mail\Entity\EntityBaseInterface;
use KTXF\Mail\Object\AddressInterface; use KTXF\Mail\Object\AddressInterface;
use KTXF\Resource\BinaryResource;
use KTXF\Resource\Delta\Delta; use KTXF\Resource\Delta\Delta;
use KTXF\Resource\Filter\IFilter; use KTXF\Resource\Filter\IFilter;
use KTXF\Resource\Identifier\EntityIdentifierInterface;
use KTXF\Resource\Provider\ResourceServiceBaseInterface; use KTXF\Resource\Provider\ResourceServiceBaseInterface;
use KTXF\Resource\Range\IRange; use KTXF\Resource\Range\IRange;
use KTXF\Resource\Range\RangeType; use KTXF\Resource\Range\RangeType;
use KTXF\Resource\Sort\ISort; use KTXF\Resource\Sort\ISort;
/** /**
* Mail Service Base Interface * Service Base Interface
* *
* Core interface for mail services with full protocol support (IMAP, JMAP, EWS, ActiveSync, Gmail API, etc.) * Minimum interface for a service, providing read-only access to collections and entities.
* Provides identity, addressing, capability information, and collection/message operations.
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
@@ -70,9 +72,9 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public const CAPABILITY_ENTITY_SORT_DATE_SENT = 'sent'; public const CAPABILITY_ENTITY_SORT_DATE_SENT = 'sent';
public const CAPABILITY_ENTITY_SORT_SIZE = 'size'; public const CAPABILITY_ENTITY_SORT_SIZE = 'size';
public const JSON_TYPE = 'mail.service'; public const JSON_TYPE = 'mail:service';
public const JSON_PROPERTY_PRIMARY_ADDRESS = 'primaryAddress'; public const PROPERTY_PRIMARY_ADDRESS = 'primaryAddress';
public const JSON_PROPERTY_SECONDARY_ADDRESSES = 'secondaryAddresses'; public const PROPERTY_SECONDARY_ADDRESSES = 'secondaryAddresses';
/** /**
* Gets the primary mailing address for this service * Gets the primary mailing address for this service
@@ -111,7 +113,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* @param IFilter|null $filter Optional filter criteria * @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order * @param ISort|null $sort Optional sort order
* *
* @return array<string|int,CollectionBaseInterface> Collections indexed by ID * @return array<string|int,CollectionBaseInterface> Collections indexed by identifier
*/ */
public function collectionList(string|int $location, ?IFilter $filter = null, ?ISort $sort = null): array; public function collectionList(string|int $location, ?IFilter $filter = null, ?ISort $sort = null): array;
@@ -138,9 +140,9 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int ...$identifiers Collection IDs to check * @param string|int ...$identifiers Collection identifiers to check
* *
* @return array<string|int,bool> Map of ID => exists * @return array<string|int,bool> Map of identifier => exists
*/ */
public function collectionExtant(string|int $location, string|int ...$identifiers): array; public function collectionExtant(string|int $location, string|int ...$identifiers): array;
@@ -149,44 +151,44 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $identifier Collection ID * @param string|int $identifier Collection identifier
* *
* @return CollectionBaseInterface|null Collection or null if not found * @return CollectionBaseInterface|null Collection or null if not found
*/ */
public function collectionFetch(string|int $identifier): ?CollectionBaseInterface; public function collectionFetch(string|int $identifier): ?CollectionBaseInterface;
/** /**
* Lists messages in a collection * Lists entities in a collection
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param IFilter|null $filter Optional filter criteria * @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order * @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination * @param IRange|null $range Optional pagination
* @param array|null $properties Optional message properties to fetch * @param array|null $properties Optional entity properties to fetch
* *
* @return array<string|int,EntityBaseInterface> Messages indexed by ID * @return array<string,EntityBaseInterface> Entities indexed by Urn
*/ */
public function entityList(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array; public function entityListBulk(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): array;
/** /**
* Lists messages in a collection * Lists entities in a collection
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param IFilter|null $filter Optional filter criteria * @param IFilter|null $filter Optional filter criteria
* @param ISort|null $sort Optional sort order * @param ISort|null $sort Optional sort order
* @param IRange|null $range Optional pagination * @param IRange|null $range Optional pagination
* @param array|null $properties Optional message properties to fetch * @param array|null $properties Optional entity properties to fetch
* *
* @return Generator Yields messages one by one as EntityBaseInterface * @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; public function entityListStream(string|int $collection, ?IFilter $filter = null, ?ISort $sort = null, ?IRange $range = null, ?array $properties = null): Generator;
/** /**
* Creates a filter builder for messages * Creates a filter builder for entities
* *
* @since 2025.05.01 * @since 2025.05.01
* *
@@ -195,7 +197,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListFilter(): IFilter; public function entityListFilter(): IFilter;
/** /**
* Creates a sort builder for messages * Creates a sort builder for entities
* *
* @since 2025.05.01 * @since 2025.05.01
* *
@@ -204,7 +206,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListSort(): ISort; public function entityListSort(): ISort;
/** /**
* Creates a range builder for messages * Creates a range builder for entities
* *
* @since 2025.05.01 * @since 2025.05.01
* *
@@ -215,40 +217,60 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
public function entityListRange(RangeType $type): IRange; public function entityListRange(RangeType $type): IRange;
/** /**
* Gets incremental changes since last sync * Gets incremental changes since last signature
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param string $signature Sync token from previous sync * @param string $signature Token from previous delta
* @param string $detail Detail level: 'ids', 'minimal', 'full'
* *
* @return array ['signature' => string, 'added' => array, 'modified' => array, 'removed' => array] * @return array ['signature' => string, 'added' => array, 'modified' => array, 'removed' => array]
*/ */
public function entityDelta(string|int $collection, string $signature, string $detail = 'ids'): Delta; public function entityDelta(string|int $collection, string $signature): Delta;
/** /**
* Checks if messages exist * Checks if entities exist
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param string|int $collection Collection identifier
* @param string|int ...$identifiers Message IDs to check * @param string|int ...$identifiers Entity identifiers to check
* *
* @return array<string|int,bool> Map of ID => exists * @return array<string|int,bool> Map of identifier => exists
*/ */
public function entityExtant(string|int $collection, string|int ...$identifiers): array; public function entityExtant(string|int $collection, string|int ...$identifiers): array;
/** /**
* Fetches one or more entities * Fetches one or more entities as an array
* *
* @since 2025.05.01 * @since 2025.05.01
* *
* @param string|int $collection Collection ID * @param EntityIdentifierInterface ...$identifiers Entity identifiers to fetch
* @param string|int ...$identifiers Message IDs to fetch
* *
* @return array<string|int,EntityBaseInterface> Messages indexed by ID * @return array<string,EntityBaseInterface> Entities indexed by Urn
*/ */
public function entityFetch(string|int $collection, string|int ...$identifiers): array; 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;
/**
* 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,10 +15,9 @@ use KTXF\Mail\Collection\CollectionPropertiesBaseInterface;
use KTXF\Resource\Identifier\CollectionIdentifier; use KTXF\Resource\Identifier\CollectionIdentifier;
/** /**
* Mail Service Collection Mutable Interface * 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 * @since 2025.05.01
*/ */
@@ -12,10 +12,9 @@ namespace KTXF\Mail\Service;
use KTXF\Resource\Provider\ResourceServiceConfigureInterface; use KTXF\Resource\Provider\ResourceServiceConfigureInterface;
/** /**
* Mail Service Configurable Interface * 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 * @since 2025.05.01
*/ */
@@ -16,10 +16,9 @@ use KTXF\Resource\Identifier\CollectionIdentifier;
use KTXF\Resource\Identifier\EntityIdentifier; use KTXF\Resource\Identifier\EntityIdentifier;
/** /**
* Mail Service Entity Mutable Interface * 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 * @since 2025.05.01
*/ */
@@ -66,6 +65,20 @@ interface ServiceEntityMutableInterface {
*/ */
public function entityModify(EntityIdentifier $target, MessagePropertiesMutableInterface $properties): EntityBaseInterface; 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 * Deletes entities
* *
@@ -81,20 +94,6 @@ interface ServiceEntityMutableInterface {
*/ */
public function entityDelete(EntityIdentifier ...$targets): array; 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 * Moves entities to another collection
* *
@@ -0,0 +1,54 @@
<?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;
}
@@ -1,48 +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\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,10 +13,9 @@ use KTXF\Mail\Object\AddressInterface;
use KTXF\Resource\Provider\ResourceServiceMutateInterface; use KTXF\Resource\Provider\ResourceServiceMutateInterface;
/** /**
* Mail Service Mutable Interface * 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 * @since 2025.05.01
*/ */
@@ -0,0 +1,52 @@
<?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 // Copy all property values
$this->properties->setLabel($value->getLabel()); $this->properties->setLabel($value->getLabel());
$this->properties->setDescription($value->getDescription()); $this->properties->setDescription($value->getDescription());
$this->properties->setPriority($value->getPriority()); $this->properties->setRank($value->getRank());
$this->properties->setVisibility($value->getVisibility()); $this->properties->setVisibility($value->getVisibility());
$this->properties->setColor($value->getColor()); $this->properties->setColor($value->getColor());
@@ -20,17 +20,15 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
*/ */
abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements CollectionPropertiesBaseInterface { abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements CollectionPropertiesBaseInterface {
public const JSON_TYPE = CollectionPropertiesBaseInterface::JSON_TYPE;
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function content(): array { public function content(): array {
$content = $this->data[self::JSON_PROPERTY_CONTENT] ?? null; $content = $this->data[self::PROPERTY_CONTENT] ?? null;
if (is_array($content)) { if (is_array($content)) {
return array_map(function ($item) { return array_filter(array_map(function ($item) {
return new CollectionContent($item); return $item instanceof CollectionContent ? $item : CollectionContent::tryFrom($item);
}, $content); }, $content));
} else { } else {
return []; return [];
} }
@@ -40,35 +38,35 @@ abstract class CollectionPropertiesBaseAbstract extends NodePropertiesBaseAbstra
* @inheritDoc * @inheritDoc
*/ */
public function getLabel(): string { public function getLabel(): string {
return $this->data[self::JSON_PROPERTY_LABEL] ?? ''; return $this->data[self::PROPERTY_LABEL] ?? '';
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getDescription(): ?string { public function getDescription(): ?string {
return $this->data[self::JSON_PROPERTY_DESCRIPTION] ?? null; return $this->data[self::PROPERTY_DESCRIPTION] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getPriority(): ?int { public function getRank(): ?int {
return $this->data[self::JSON_PROPERTY_PRIORITY] ?? null; return $this->data[self::PROPERTY_RANK] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getVisibility(): ?bool { public function getVisibility(): ?bool {
return $this->data[self::JSON_PROPERTY_VISIBILITY] ?? null; return $this->data[self::PROPERTY_VISIBILITY] ?? null;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function getColor(): ?string { public function getColor(): ?string {
return $this->data[self::JSON_PROPERTY_COLOR] ?? null; return $this->data[self::PROPERTY_COLOR] ?? null;
} }
} }
@@ -10,18 +10,17 @@ declare(strict_types=1);
namespace KTXF\People\Collection; namespace KTXF\People\Collection;
use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface; use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
use PhpParser\Node\Expr\Array_;
interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface { interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'people:collection'; public const JSON_TYPE = 'people:collection';
public const JSON_PROPERTY_CONTENT = 'content'; public const PROPERTY_CONTENT = 'content';
public const JSON_PROPERTY_LABEL = 'label'; public const PROPERTY_LABEL = 'label';
public const JSON_PROPERTY_DESCRIPTION = 'description'; public const PROPERTY_DESCRIPTION = 'description';
public const JSON_PROPERTY_PRIORITY = 'priority'; public const PROPERTY_RANK = 'rank';
public const JSON_PROPERTY_VISIBILITY = 'visibility'; public const PROPERTY_VISIBILITY = 'visibility';
public const JSON_PROPERTY_COLOR = 'color'; public const PROPERTY_COLOR = 'color';
/** /**
* Gets the content type of this collection * Gets the content type of this collection
@@ -47,11 +46,11 @@ interface CollectionPropertiesBaseInterface extends NodePropertiesBaseInterface
public function getDescription(): ?string; public function getDescription(): ?string;
/** /**
* Gets the priority of this collection * Gets the rank of this collection
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
public function getPriority(): ?int; public function getRank(): ?int;
/** /**
* Gets the visibility of this collection * Gets the visibility of this collection
@@ -33,7 +33,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setLabel(string $value): static { public function setLabel(string $value): static {
$this->data[self::JSON_PROPERTY_LABEL] = $value; $this->data[self::PROPERTY_LABEL] = $value;
return $this; return $this;
} }
@@ -41,15 +41,15 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setDescription(?string $value): static { public function setDescription(?string $value): static {
$this->data[self::JSON_PROPERTY_DESCRIPTION] = $value; $this->data[self::PROPERTY_DESCRIPTION] = $value;
return $this; return $this;
} }
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function setPriority(?int $value): static { public function setRank(?int $value): static {
$this->data[self::JSON_PROPERTY_PRIORITY] = $value; $this->data[self::PROPERTY_RANK] = $value;
return $this; return $this;
} }
@@ -57,7 +57,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setVisibility(?bool $value): static { public function setVisibility(?bool $value): static {
$this->data[self::JSON_PROPERTY_VISIBILITY] = $value; $this->data[self::PROPERTY_VISIBILITY] = $value;
return $this; return $this;
} }
@@ -65,7 +65,7 @@ abstract class CollectionPropertiesMutableAbstract extends CollectionPropertiesB
* @inheritDoc * @inheritDoc
*/ */
public function setColor(?string $value): static { public function setColor(?string $value): static {
$this->data[self::JSON_PROPERTY_COLOR] = $value; $this->data[self::PROPERTY_COLOR] = $value;
return $this; return $this;
} }
@@ -30,11 +30,11 @@ interface CollectionPropertiesMutableInterface extends NodePropertiesMutableInte
public function setDescription(?string $value): static; public function setDescription(?string $value): static;
/** /**
* Sets the priority of this collection * Sets the rank of this collection
* *
* @since 2025.05.01 * @since 2025.05.01
*/ */
public function setPriority(?int $value): static; public function setRank(?int $value): static;
/** /**
* Sets the visibility of this collection * Sets the visibility of this collection
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace KTXF\People\Entity; namespace KTXF\People\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeBaseAbstract; use KTXF\Resource\Provider\Node\NodeBaseAbstract;
/** /**
@@ -20,7 +21,12 @@ use KTXF\Resource\Provider\Node\NodeBaseAbstract;
*/ */
abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface { abstract class EntityBaseAbstract extends NodeBaseAbstract implements EntityBaseInterface {
protected EntityPropertiesBaseAbstract $properties; 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]);
}
/** /**
* @inheritDoc * @inheritDoc
@@ -13,8 +13,6 @@ use KTXF\Resource\Provider\Node\NodeBaseInterface;
interface EntityBaseInterface extends NodeBaseInterface { interface EntityBaseInterface extends NodeBaseInterface {
public const JSON_TYPE = 'people:entity';
/** /**
* Gets the entity properties * Gets the entity properties
* *
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace KTXF\People\Entity; namespace KTXF\People\Entity;
use KTXF\Resource\Identifier\EntityIdentifier;
use KTXF\Resource\Provider\Node\NodeMutableAbstract; use KTXF\Resource\Provider\Node\NodeMutableAbstract;
use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface; use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
@@ -21,9 +22,12 @@ use KTXF\Resource\Provider\Node\NodePropertiesMutableInterface;
*/ */
abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface { abstract class EntityMutableAbstract extends NodeMutableAbstract implements EntityMutableInterface {
public const JSON_TYPE = EntityMutableInterface::JSON_TYPE; protected string $type = 'people:entity';
protected EntityPropertiesMutableInterface $properties;
protected EntityPropertiesMutableAbstract $properties; 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]);
}
/** /**
* @inheritDoc * @inheritDoc
@@ -40,7 +44,7 @@ abstract class EntityMutableAbstract extends NodeMutableAbstract implements Enti
throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface'); throw new \InvalidArgumentException('Properties must implement EntityPropertiesMutableInterface');
} }
$this->properties->setDataRaw($value->getDataRaw()); $this->properties = $value;
return $this; return $this;
} }
@@ -16,8 +16,6 @@ use KTXF\Resource\Provider\Node\NodeMutableInterface;
*/ */
interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface { interface EntityMutableInterface extends EntityBaseInterface, NodeMutableInterface {
public const JSON_TYPE = EntityBaseInterface::JSON_TYPE;
/** /**
* Gets the entity properties (mutable) * Gets the entity properties (mutable)
* *
@@ -13,9 +13,10 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract;
abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface { abstract class EntityPropertiesBaseAbstract extends NodePropertiesBaseAbstract implements EntityPropertiesBaseInterface {
public const JSON_TYPE = EntityPropertiesBaseInterface::JSON_TYPE; /**
* @inheritDoc
*/
public function getDataRaw(): array|string|null { public function getDataRaw(): array|string|null {
return $this->data[self::JSON_PROPERTY_DATA] ?? null; return $this->data[self::PROPERTY_DATA] ?? null;
} }
} }
@@ -14,7 +14,7 @@ use KTXF\Resource\Provider\Node\NodePropertiesBaseInterface;
interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface { interface EntityPropertiesBaseInterface extends NodePropertiesBaseInterface {
public const JSON_TYPE = 'people:entity'; public const JSON_TYPE = 'people:entity';
public const JSON_PROPERTY_DATA = 'data'; public const PROPERTY_DATA = 'data';
public function getDataRaw(): array|string|null; public function getDataRaw(): array|string|null;
@@ -24,7 +24,7 @@ abstract class EntityPropertiesMutableAbstract extends EntityPropertiesBaseAbstr
} }
public function setDataRaw(array|string|null $value): static { public function setDataRaw(array|string|null $value): static {
$this->data[self::JSON_PROPERTY_DATA] = $value; $this->data[self::PROPERTY_DATA] = $value;
return $this; return $this;
} }
} }

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