This repository was archived by the owner on Jul 8, 2025. It is now read-only.
[pull] develop from eXist-db:develop#383
Open
pull[bot] wants to merge 1879 commits into
Open
Conversation
…g to a heap byte[] xmldb:store / xmldb:store-as-binary stored a base64Binary item by calling BinaryValue.toJavaObject(), which reads the entire value into a heap byte[]. For a large binary -- e.g. xmldb:store-as-binary($c, $n, request:get-data()) piping a multi-GB upload -- that materializes the whole resource in memory before storing, risking OutOfMemoryError. Pass the BinaryValue through to the resource instead. LocalBinaryResource keeps the BinaryValue, and LocalCollection.storeBinaryResource streams it through the binary cache (disk-backed by default: FileFilterInputStreamCache in conf.xml) rather than holding it on the heap. Correctness is unchanged (byte-identical store/readback); covered by existing binary tests (XqueryApiTest, RestBinariesTest, XmldbBinariesTest). Note the store path calls getStreamLength() (a counting pass) before streaming, so the value is traversed twice -- both through the disk-backed cache, not the heap. True zero-copy (raw request stream -> broker.storeDocument, no cache) is the separate request:get-input-stream() follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tent-Disposition test Address @line-o's review on #6466: every failure path now carries a fitting error code instead of a bare XPathException, and the 3-arg (filename) form has explicit Content-Disposition coverage. - Resource is not a binary document -> XPTY0004 (a type error, per the review). - Invalid resource path / permission denied -> FODC0002 ("Error retrieving resource"), the same code already used for the not-found case, so all three "cannot get the resource" conditions share one code. - Transaction / IO error while streaming -> new EXXQDY0007 ("I/O error while streaming a binary resource to the response"), in the eXist error namespace. - RestBinariesTest: add streamBinaryResourceWithFilename, asserting the 3-arg form sends Content-Disposition: inline; filename="..." with a byte-identical body. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…esource [feature] response:stream-binary-resource — zero-copy binary download
… call The XQuery 3.1 arrow operator `EXPR => f(args)` is defined as the call `f(EXPR, args)`, but eXist evaluated every arrow — including the statically-named case — by building a dynamic FunctionReference: it pre-evaluated the left-hand side, injected it as a placeholder (`ContextParam extends Function.Placeholder`), then re-analyzed and dynamically dispatched on every evaluation. That caused three bugs and a per-call performance penalty. Compile the named arrow to a real FunctionCall instead, via FunctionFactory.createFunction(qname, [leftExpr, parameters...]) — the same path the parser uses for an ordinary call — wrapping in a PartialFunctionApplication when a `?` placeholder is present. A genuine higher-order right-hand side (a variable or parenthesized expression yielding a function) keeps the dynamic path. A second change was needed in the tree-walker: the arrowOp rule had no `QUESTION -> Function.Placeholder` branch (unlike functionCall), so a `?` in an arrow argument list never became a placeholder. Because the left-hand side is now a genuine argument expression rather than a pre-evaluated placeholder, this: - Closes #3887 — a context-sensitive callee (e.g. util:eval) now sees in-scope variables, like the direct call. - Closes #3336 — `EXPR => f(?)` is now a partial application of the remaining arity, not a wrong-arity call. - Closes #3885 — no NullPointerException for a nested partial application after the arrow. It also corrects a conformance gap: the arrow now applies function-conversion rules like the equivalent direct call, so `(1,2,3) => f()` into an `xs:string*` parameter raises XPTY0004 instead of silently coercing — matching Saxon and the direct call. The arrowop.xql test that asserted the old lenient result is updated accordingly. W3C XQTS HEAD prod-ArrowPostfix improves 41/42 -> 42/42 (ArrowPostfix-108), with no regression in the function/HoF/sequence-type sets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ArrowOperatorBenchmark compares `$x => f()` against the equivalent `f($x)` (a single call and a 3-arrow chain) so the arrow's per-call dispatch overhead is measured and guarded against regression. It mirrors the existing engine-level benchmarks (e.g. AxisBenchmark) with a BrokerPool @setup. Before the direct-call fix, the arrow's dynamic FunctionReference dispatch cost ~2.2x over a direct call for a single arrow and ~3.2x across a 3-arrow chain; after, the arrow is at parity with the direct call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er accessor A tail-recursive XQuery function is evaluated lazily through a DeferredFunctionCall (eXist's tail-call trampoline). When its deferred body raises an error, DeferredFunctionCall caches the exception and re-throws it on every later access. Its non-throwing Sequence accessor methods (getCardinality, isEmpty, hasOne, getItemCountLong, itemAt, ...) cannot propagate the checked XPathException, so each catches it and returns a default -- but each also logged it. A consumer that inspects the same deferred value through several accessors (the serializer and the html-templating engine both do) therefore logged the same failure once per accessor; and because XPathException.getMessage() includes the accumulated "In function:" call stack, each line is long, so one error became many long lines. Route every catch through a captureAndLogOnce(...) helper that logs only on the first capture. Behavior is otherwise unchanged: the failure is still cached, the non-throwing accessors still return the same defaults, and the error still surfaces to the caller through the throwing accessors (iterate, getStringValue), which the new regression test asserts along with logged-once and body-runs-once. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iew) Address @line-o's review on PR #6469: invert the branch in analyze() and resetState() to handle the short dynamic case first and return early, leaving the larger statically-named-call block un-nested; and fix the brace-on-next-line on the cardinality check in eval() to standard K&R. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per @line-o's review on PR #6469: alongside ao:type-checks-user-func (which asserts XPTY0004 because xs:integer items do not convert to a declared xs:string* parameter), add two passing companions proving the arrow itself is sound when the types line up: one passing string input, and one keeping integer input but casting to string inside an xs:anyAtomicType* RHS function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add two XQSuite regression tests to arrowop.xql for argument-placeholder behavior on the right-hand side of the arrow operator, prompted by #6471: - ao:placeholder-exceeds-arity: 1 => xs:string(?) is xs:string(1, ?), and xs:string#2 does not exist, so it raises XPST0017. This arity-overflow case was not previously covered. - ao:placeholder-partial-application-variadic: ("1" => concat(?))("1") yields "11", exercising a partial application over the variadic fn:concat (the existing ao:placeholder-partial-application covers fixed arity via fn:substring). All three behaviors from #6471 already work on current develop; these tests pin them. Full XQuery3 suite green (1063 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…elogging [bugfix] Log a deferred (tail-call) function's error once, not once per Sequence accessor
Without this change the test suite reproducibly failed with cannot bind to 0.0.0.0:8088
Return early and use new switch-statement in order to simplify the flow within FunctionFactory.createFunction
Introduce new private method functionNotFoundErrorDescription in order to make resolveForwardRefences easier to understand.
* Return early in order to reduce indentation. * Fix comments * Fix formatting
Two separate instances of the same code that ran after FunctionFactory.createFunction indicated that it might be better to have both pieces run the same code to counter drift. The new method FunctionFactory.createFunctionCall ensures that the RHS of ArrowOperator is treated as other functionCalls in XQuery.
writeResultJSON never called response.setContentType(), so REST query results serialized as JSON (a POST <query method="json"> envelope) were returned without a Content-Type and output:media-type was silently ignored on that path — clients received JSON under the wrong media type (the "XML Parsing Error in console" class of failure). The XML writer (writeResultXML) already honors output:media-type. Set the Content-Type in writeResultJSON before the output stream is opened: default to application/json, but honor an explicit (non-XML- default) output:media-type. The logic lives in a small helper so the method gains no extra branching. Add a reusable MimeType.JSON_TYPE constant alongside XML_TYPE/HTML_TYPE/TEXT_TYPE. Tests (RESTServiceTest): postQueryJsonContentType asserts the default application/json; postQueryJsonExplicitMediaType asserts an explicit media-type is honored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps `greenmail.version` from 2.1.8 to 2.1.9. Updates `com.icegreen:greenmail-junit4` from 2.1.8 to 2.1.9 - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](greenmail-mail-test/greenmail@release-2.1.8...release-2.1.9) Updates `com.icegreen:greenmail` from 2.1.8 to 2.1.9 - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](greenmail-mail-test/greenmail@release-2.1.8...release-2.1.9) --- updated-dependencies: - dependency-name: com.icegreen:greenmail-junit4 dependency-version: 2.1.9 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: com.icegreen:greenmail dependency-version: 2.1.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [net.sf.xmldb-org:xmldb-api](https://github.com/xmldb-org/xmldb-api) from 2.0.0 to 2.1.0. - [Release notes](https://github.com/xmldb-org/xmldb-api/releases) - [Commits](xmldb-org/xmldb-api@xmldb-api-2.0...xmldb-api-2.1) --- updated-dependencies: - dependency-name: net.sf.xmldb-org:xmldb-api dependency-version: 2.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [com.zaxxer:HikariCP](https://github.com/brettwooldridge/HikariCP) from 7.0.2 to 7.1.0. - [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES) - [Commits](brettwooldridge/HikariCP@HikariCP-7.0.2...HikariCP-7.1.0) --- updated-dependencies: - dependency-name: com.zaxxer:HikariCP dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Adds a "Choosing XQSuite vs Java tests" subsection to the testing conventions: default to XQSuite for XQuery-level behavior, and reach for Java only when XQSuite structurally can't express or exercise the behavior (pure-Java units, request/ response/session context that needs a live HTTP request, the HTTP/transport layer itself, or Java-level wiring such as broker pool / locking / transactions). Within Java, use the lightest vehicle that exercises the real behavior. Cites the request-module content-negotiation work (#6477) as precedent. AGENTS.md is the canonical, repo-rooted home for this guidance so it is visible to sessions regardless of which repo they are rooted in. Also removes the "Known Issues" section, whose three entries were all stale; two were never true: - groupby.collation "flaky" / ArrayIndexOutOfBoundsException: unsubstantiated. No issue, PR, commit, or CI evidence backs it; the test is deterministic and passes. Asserting an unbacked "known flake" risks agents dismissing real CI failures. - fn:filter / issue #3382: fixed. #3382 is closed and fn:filter now raises XPTY0004 when the predicate function does not return xs:boolean. - fn:doc() file:// restriction: unsubstantiated. DocUtils already routed file:/URL paths through SourceFactory when this entry was added (2026-03-15), so fn:doc could load file: documents all along; the later #6207 work only added security-gating, it did not lift a block that never existed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The four string operation optimization functions startsWith, endsWith, contains and equals where doing almost the same thing. They are now generalized into optimizeStringFunction.
[bugfix] RESTServer: set Content-Type for JSON results
…questFunctionTest Address review feedback (dizzzz): switch the two java.io.ByteArrayOutputStream uses in the test's HTTP endpoints to commons-io's UnsynchronizedByteArrayOutputStream, the house convention (already used elsewhere in this file), for memory efficiency. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThoADnh6VvDt5w8kz7d2d3
The previous commit's switch to commons-io UnsynchronizedByteArrayOutputStream in SendRequestFunctionTest used commons-io without declaring it, which the maven-dependency-plugin analyze gate (failOnWarning) rejects as an undeclared, non-test-scoped dependency. Declare commons-io at test scope, mirroring extensions/modules/file/pom.xml. Version is managed by the parent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThoADnh6VvDt5w8kz7d2d3
…olution Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ponse [bugfix] http-client: byte-safe multipart response parsing with nesting
…ldb: base XmldbURI#getURI strips the xmldb: prefix for xmldb:exist:// URIs but keeps it for the short xmldb:/db/... form, so unconditionally prepending it yielded xmldb:xmldb:/db/... A resolved stylesheet is now its own system id, so this was reached by any relative xsl:import within an imported stylesheet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… stylesheet RFC 3986 discards the last segment of the base, which is correct for a document but not for a collection: resolving style.xsl against the collection /db/apps/app yielded /db/apps/style.xsl. A collection and a document are not distinguishable by path alone, so the absence of an extension in the last segment is taken to mean a collection. Outside the database RFC 3986 still applies unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps the junit-jupiter-bom group with 4 updates: [org.junit:junit-bom](https://github.com/junit-team/junit-framework), [org.junit.platform:junit-platform-engine](https://github.com/junit-team/junit-framework), [org.junit.jupiter:junit-jupiter-engine](https://github.com/junit-team/junit-framework) and [org.junit.vintage:junit-vintage-engine](https://github.com/junit-team/junit-framework). Updates `org.junit:junit-bom` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) Updates `org.junit.platform:junit-platform-engine` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) Updates `org.junit.jupiter:junit-jupiter-engine` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) Updates `org.junit.vintage:junit-vintage-engine` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) Updates `org.junit.platform:junit-platform-engine` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) Updates `org.junit.jupiter:junit-jupiter-engine` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) Updates `org.junit.vintage:junit-vintage-engine` from 6.1.1 to 6.1.2 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](junit-team/junit-framework@r6.1.1...r6.1.2) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom - dependency-name: org.junit.platform:junit-platform-engine dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom - dependency-name: org.junit.vintage:junit-vintage-engine dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom - dependency-name: org.junit.platform:junit-platform-engine dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom - dependency-name: org.junit.vintage:junit-vintage-engine dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: junit-jupiter-bom ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [org.bouncycastle:bcprov-jdk18on](https://github.com/bcgit/bc-java) from 1.84 to 1.85. - [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html) - [Commits](https://github.com/bcgit/bc-java/commits) --- updated-dependencies: - dependency-name: org.bouncycastle:bcprov-jdk18on dependency-version: '1.85' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
…tle-bcprov-jdk18on-1.85 Bump org.bouncycastle:bcprov-jdk18on from 1.84 to 1.85
…-bom-7b9e9837d1 Bump the junit-jupiter-bom group with 4 updates
Set a Jetty stop timeout so shutdown does not block indefinitely on active connections; move ShutdownListenerImpl.shutdown() to a daemon thread so it does not deadlock the Jetty shutdown-hook thread that also holds the BrokerPool write lock.
Bumps [org.apache.mina:mina-core](https://github.com/apache/mina) from 2.1.12 to 2.1.13. - [Commits](apache/mina@2.1.12...2.1.13) --- updated-dependencies: - dependency-name: org.apache.mina:mina-core dependency-version: 2.1.13 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Fix Jetty shutdown hang and BrokerPool read-only race
…buggee/org.apache.mina-mina-core-2.1.13
Bumps the actions group with 3 updates: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action), [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) and [softprops/action-gh-release](https://github.com/softprops/action-gh-release). Updates `docker/setup-qemu-action` from 4.1.0 to 4.2.0 - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](docker/setup-qemu-action@0611638...96fe6ef) Updates `docker/setup-buildx-action` from 4.1.0 to 4.2.0 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](docker/setup-buildx-action@d7f5e7f...bb05f3f) Updates `softprops/action-gh-release` from 3.0.1 to 3.0.2 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](softprops/action-gh-release@718ea10...3d0d988) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: docker/setup-buildx-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: softprops/action-gh-release dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com>
…ons-6fb1bc60f3 Bump the actions group with 3 updates
Bumps `greenmail.version` from 2.1.9 to 2.1.10. Updates `com.icegreen:greenmail-junit4` from 2.1.9 to 2.1.10 - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](greenmail-mail-test/greenmail@release-2.1.9...release-2.1.10) Updates `com.icegreen:greenmail` from 2.1.9 to 2.1.10 - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](greenmail-mail-test/greenmail@release-2.1.9...release-2.1.10) --- updated-dependencies: - dependency-name: com.icegreen:greenmail-junit4 dependency-version: 2.1.10 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: com.icegreen:greenmail dependency-version: 2.1.10 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…sion-2.1.10 Bump greenmail.version from 2.1.9 to 2.1.10
Bumps `greenmail.version` from 2.1.10 to 2.1.11. Updates `com.icegreen:greenmail-junit4` from 2.1.10 to 2.1.11 - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](greenmail-mail-test/greenmail@release-2.1.10...release-2.1.11) Updates `com.icegreen:greenmail` from 2.1.10 to 2.1.11 - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](greenmail-mail-test/greenmail@release-2.1.10...release-2.1.11) --- updated-dependencies: - dependency-name: com.icegreen:greenmail-junit4 dependency-version: 2.1.11 dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: com.icegreen:greenmail dependency-version: 2.1.11 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…sion-2.1.11 Bump greenmail.version from 2.1.10 to 2.1.11
[bugfix] Bind context item for trailing function-call path step on stored docs
…ment A text/html response was XML-parsed and, on failure, returned as a raw string, so non-well-formed HTML (the common case) was no longer a navigable document. ResponseHandler now falls back to eXist's configured HTML-to-XML parser (NekoHTML, via HtmlToXmlParser -- the same parser util:parse-html uses) to turn tag-soup HTML into a document node. Well-formed XHTML still parses directly as XML; if no HTML parser is configured or it cannot parse the input, the body degrades to a string as before, so the change is strictly additive. The test conf.xml gains the html-to-xml parser block (matching the default eXist configuration), and SendRequestFunctionTest gains malformedHtmlResponseIsParsedToDocument. Part of #6512 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dler
The HTML-response change uses HtmlToXmlParser, whose result is a
com.evolvedbinary.j8fu.Either, so ResponseHandler now references j8fu directly.
It was only available transitively via exist-core, which fails the
maven-dependency-plugin analyze-only check ("used, undeclared dependency").
Declare j8fu at compile scope (version managed by exist-parent), matching how
the file/mail/exi modules already declare it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[bugfix] http-client: parse a non-well-formed HTML response to a document
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.1)
Can you help keep this open source service alive? 💖 Please sponsor : )