Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e76b8f7
docs: design review 22 remediation
mlischetti Sep 2, 2026
1b3a19a
docs: plan review 22 remediation
mlischetti Sep 2, 2026
46b947d
fix(native-lib): lease engines across admitted operations
mlischetti Sep 3, 2026
59e4340
fix(native-lib): contain C entrypoint exceptions
mlischetti Sep 3, 2026
09071f8
test(native-lib): cover C ABI failure and lease contracts
mlischetti Sep 3, 2026
7e9cf33
test(native-lib): enforce destroy lease ordering
mlischetti Sep 3, 2026
17f18a5
test(native-lib): observe closed engine admission
mlischetti Sep 3, 2026
9e0ec26
test(native-lib): observe native destroy return
mlischetti Sep 3, 2026
78c3a44
test(native-lib): restore bounded destroy drain check
mlischetti Sep 3, 2026
5b383b2
refactor(native-lib): remove unleased runtime lookup
mlischetti Sep 3, 2026
2444240
fix(python): reject native callback reentrancy
mlischetti Sep 4, 2026
9e9aca3
fix(python): reject native callback reentrancy
mlischetti Sep 4, 2026
0aba972
fix(python): reject native callback reentrancy
mlischetti Sep 4, 2026
bd6a375
fix(python): contain callback base exceptions
mlischetti Sep 4, 2026
789b711
fix(python): harden callback admission
mlischetti Sep 4, 2026
efa6f29
fix(python): normalize callback status ABI
mlischetti Sep 4, 2026
aca8a82
fix(python): bind operations to engine generations
mlischetti Sep 7, 2026
256238c
test(python): update operation token fakes
mlischetti Sep 7, 2026
1c84b95
fix(python): wake all operation waiters
mlischetti Sep 7, 2026
ab903ac
fix(node): reject native callback reentrancy
mlischetti Sep 7, 2026
c3eb13f
test(node): update callback admission regressions
mlischetti Sep 7, 2026
3030a1e
test(node): clarify callback admission coverage
mlischetti Sep 7, 2026
ad370a3
test(node): preserve lifecycle regression coverage
mlischetti Sep 7, 2026
22c3805
fix(node): bind lazy streams to engine generations
mlischetti Sep 7, 2026
6837818
test(node): assert exact stale generation errors
mlischetti Sep 7, 2026
6cdfa99
fix(node): validate streams around serialization
mlischetti Sep 7, 2026
1ea48a6
fix(node): propagate streaming consumer credits
mlischetti Sep 7, 2026
8a1e511
fix(node): avoid duplicate stream chunk copies
mlischetti Sep 7, 2026
f985d4a
fix(node): harden streaming finalization
mlischetti Sep 7, 2026
bc24ed0
fix(node): close lazy streaming iterators safely
mlischetti Sep 7, 2026
7733da8
fix(node): serialize transform setup cleanup
mlischetti Sep 7, 2026
8f932af
fix(node): order transform cancellation safely
mlischetti Sep 8, 2026
1ba8302
fix(node): preserve transform request FIFO
mlischetti Sep 8, 2026
215df9d
fix(node): abandon pending transform setup
mlischetti Sep 8, 2026
79e283b
fix(node): bound asynchronous output buffering
mlischetti Sep 8, 2026
9aa486b
fix(node): validate asynchronous output credits
mlischetti Sep 9, 2026
087ed5e
fix(node): harden output completion lifecycle
mlischetti Sep 9, 2026
87ed273
fix(node): fail closed on settlement clear errors
mlischetti Sep 9, 2026
cc601da
test(node): inject detach failures across isolate recovery
mlischetti Sep 10, 2026
d29168e
fix(node): isolate stale engine generations
mlischetti Sep 10, 2026
8cf1f09
fix(node): bound engine identity lifetime
mlischetti Sep 10, 2026
263bcd9
fix(node): publish detach failures before admission
mlischetti Sep 10, 2026
770be50
fix(node): finalize bridge ownership safely
mlischetti Sep 10, 2026
213c68f
test(node): detect post-reclamation finalization
mlischetti Sep 10, 2026
30810ba
docs(native-lib): document hardened multi-engine contracts
mlischetti Sep 11, 2026
f8d1d35
docs(native-lib): align lifecycle design details
mlischetti Sep 11, 2026
e3416e5
docs(native-lib): correct Python cleanup contract
mlischetti Sep 11, 2026
802cef2
docs: consolidate multi-engine hardening design
mlischetti Sep 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Design: External DataWeave Module Support in Node.js Binding

**Date:** 2026-08-04
**Status:** Superseded
**Date:** 2026-08-04
**Status:** Superseded
**Related Proposal:** [docs/proposals/nodejs-external-modules.md](../../proposals/nodejs-external-modules.md)

> **⚠️ Superseded (2026-08-31).** This document describes the original
Expand Down Expand Up @@ -366,20 +366,20 @@ public interface ResolveModuleCallback extends CFunctionPointer {
```java
public class CallbackWeaveResourceResolver implements WeaveResourceResolver {
private final ResolveModuleCallback callback;

public CallbackWeaveResourceResolver(ResolveModuleCallback callback) {
this.callback = callback;
}

@Override
public Option<WeaveResource> resolve(ResourceDescriptor descriptor) {
CCharPointer pathPtr = CTypeConversion.toCString(descriptor.path()).get();
CCharPointer resultPtr = callback.invoke(CurrentIsolate.getCurrentThread(), pathPtr);

if (resultPtr.isNull()) {
return Option.empty(); // Resolver returned null
}

String source = CTypeConversion.toJavaString(resultPtr);
// Note: host must free resultPtr after this returns
return Option.apply(new StringWeaveResource(descriptor.path(), source));
Expand Down Expand Up @@ -462,7 +462,7 @@ static char* resolve_module_callback(void* thread, const char* module_path) {
// Return result_source (or NULL)
}

static void resolver_js_callback(napi_env env, napi_value js_callback,
static void resolver_js_callback(napi_env env, napi_value js_callback,
void* context, void* data) {
// Call JS: result = resolveModule(modulePath)
// Extract result string or null
Expand Down
163 changes: 107 additions & 56 deletions docs/superpowers/specs/2026-08-07-native-lib-multi-engine-design.md

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions native-lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ isolate for the process lifetime, and lets a future initialization build a fresh
isolate. This ref-counting and teardown policy lives in the binding code, not in
the dwlib engine ABI.

## Raw engine ABI contract

The exported engine entrypoints are `create_engine`,
`create_engine_with_resolver`, `destroy_engine`, `run_script_engine`,
`run_script_callback_engine`, and `run_script_input_output_callback_engine`.
`create_engine` and `create_engine_with_resolver` return `0` when their Java C
entrypoint fails. The three `run_*_engine` entrypoints return `NULL` when their
Java C entrypoint fails; callers must not pass `NULL` to `free_cstring`. Those
sentinels are distinct from normal DataWeave failures: a script error is a
non-`NULL` JSON envelope with `success:false`, and that allocated result must be
freed normally.

`destroy_engine` closes admission for the handle and blocks until operations
already admitted to that engine drain. Resolver, read, and write callback
contexts must remain valid until `destroy_engine` returns. A callback must not
call `destroy_engine` synchronously for the engine invoking it: that operation
holds an admitted lease and would wait for itself to finish.

## Building with Gradle

### Prerequisites
Expand Down
29 changes: 26 additions & 3 deletions native-lib/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,26 @@ import { cleanup } from 'dataweave-native';
await cleanup();
```

### Callback and stream lifecycle

Resolver, read, and write callbacks must not call DataWeave lifecycle or
execution APIs on the same thread. The binding rejects that reentry with a
public `DataWeaveError` instead of recursively entering the native runtime.
Native addon callers receive the message `DataWeave native methods cannot be
called from a native callback` and code `ERR_DATAWEAVE_CALLBACK_REENTRANCY`.

Streaming and transform work captures the initialized engine generation. If
cleanup or reinitialization happens before it is consumed or admitted, it fails
with `DataWeaveError: DataWeave operation belongs to a stale engine generation.`
and never runs against the replacement engine. Cleanup cancels and waits for
abandoned active streams and transforms before destroying their engine.

The addon uses bounded native output buffering. Its byte and chunk watermarks,
finite thread-safe-function queue, and controller/sequence credit bookkeeping
are implementation details, not public configuration or a BigInt sequence API.
The bound excludes `Buffer` objects retained by application code after a chunk
is yielded.

### Class-Based API

For more control, use the `DataWeave` class directly:
Expand Down Expand Up @@ -411,8 +431,9 @@ console.log(result.getString()); // "300"
### Streaming Large Files

```javascript
import { runTransform } from 'dataweave-native';
import { readFileSync, createWriteStream } from 'fs';
import { once } from "node:events";
import { readFileSync, createWriteStream } from "node:fs";
import { runTransform } from "dataweave-native";

const script = `
%dw 2.0
Expand All @@ -439,7 +460,9 @@ const generator = runTransform(
const output = createWriteStream('filtered.json');

for await (const chunk of generator) {
output.write(chunk);
if (!output.write(chunk)) {
await once(output, "drain");
}
}

output.end();
Expand Down
Loading