Skip to content

feat: JSServerExtensionInvoker SDK — consume JS-declared server extensions from other bundles - #686

Closed
romain-pm wants to merge 1 commit into
feature/js-server-extensionsfrom
feature/js-server-extensions-sdk
Closed

feat: JSServerExtensionInvoker SDK — consume JS-declared server extensions from other bundles#686
romain-pm wants to merge 1 commit into
feature/js-server-extensionsfrom
feature/js-server-extensions-sdk

Conversation

@romain-pm

Copy link
Copy Markdown

What

Exposes a small, polyglot-free public SDK so other OSGi bundles can consume JavaScript-declared server extensions — i.e. registry entries contributed by JS modules via server.registry.add(type, key, entry) — and invoke their JS callbacks from Java.

Stacked on feature/js-server-extensions (base of this PR). One commit.

Why

The built-in registrars added by feature/js-server-extensions (action, choicelist-initializer, node-validator, render-filter) wire JS entries to Jahia's own extension points. A third-party module that wants to own its own extension type — e.g. Formidable running JS-authored form-field validators inside its submission pipeline — has no supported way to do it today: GraalVMEngine, Registry, and AbstractServiceRegistrar live in non-exported packages, and consuming them directly would also drag GraalVM polyglot types into the consumer.

First consumer: Jahia/formidable#163 (server-side JS field validators for Jahia/formidable#158).

Design

New exported package org.jahia.modules.javascript.modules.engine.sdk — the only exported package of the engine bundle:

List<T> forEach(String registryType, ExtensionHandler<T> handler);
// handler.handle(Map<String,Object> entry, Invoker invoker)
// invoker.call(callable, args…) -> null | Boolean | Long/Double | String | List | Map
  • forEach runs entirely inside one pooled GraalVM context (GraalVMEngine.doWithContext), re-resolving the registry per call — respecting the rule that JS function handles must never outlive a context (contexts are recycled on module (un)deploy).
  • Invoker.call executes a JS callable from an entry and recursively converts the result to plain Java, so no org.graalvm.polyglot type ever crosses the SDK boundary — consumers need zero GraalVM imports and no version coupling to the embedded Graal stack.
  • Registered as an OSGi @Component (JSServerExtensionInvokerImpl); consumers reference the service (optionally, for graceful degradation on older engines).
  • Deliberately not exposing AbstractServiceRegistrar/GraalVMEngine/Registry: the facade is the minimal surface, and internals stay free to evolve.

Verification

  • Engine bundle builds; manifest asserts Export-Package: org.jahia.modules.javascript.modules.engine.sdk and the DS descriptor is generated.
  • Deployed on a local Jahia 8.2.x and exercised end-to-end by feat: server-side JS field validators for extended inputs (#158) formidable#163: a formidable-field-validator registry type populated from a JS module, consumed from formidable-engine — 12/12 forged/valid submission cases behaved correctly (forged values rejected, valid accepted).
  • Changelog note included (.chachalog/js-server-extension-sdk.md, minor).

Open questions for review

  • Naming: sdk package vs spi, JSServerExtensionInvoker vs something closer to "registry consumer".
  • Whether forEach should also expose the entry's owning bundle (consumers may want provenance for diagnostics).
  • Version policy for the exported package once feature/js-server-extensions ships (currently ${project.version}).

…nsions

Adds a public, polyglot-free SDK so other OSGi bundles can consume JS-declared
server extensions (server.registry.add(type,...)) and invoke their callbacks:
- JSServerExtensionInvoker (interface) + Impl (@component) in a new exported
  package org.jahia.modules.javascript.modules.engine.sdk
- forEach(type, handler) runs within one pooled GraalVM context; Invoker.call
  executes a JS callable and converts the result to plain Java (no GraalVM types
  leak to consumers, so callers need no polyglot dependency)
- engine bundle now Export-Package's only the sdk package

Enables Formidable to run JS-authored form-field validators server-side (issue
Jahia/formidable#158) without embedding GraalVM or engine internals.
@romain-pm

Copy link
Copy Markdown
Author

Folded into #687 for combined review: the SDK commit (fd7a229) was cherry-picked onto feature/js-server-extensions as b7930af (identical patch-id b168261), so the JSServerExtensionInvoker SDK, the server extension points, and the content-patches feature all review together in #687. Closing this stacked PR.

@romain-pm romain-pm closed this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant