fjs 2.1.0 copy "fjs: ^2.1.0" to clipboard
fjs: ^2.1.0 copied to clipboard

A high-performance JavaScript runtime for Flutter applications, built with Rust and powered by QuickJS.

FJS Logo

πŸš€ FJS - Flutter JavaScript Engine #

High-performance JavaScript runtime for Flutter ⚑ Built with Rust and powered by QuickJS πŸ¦€

pub package GitHub stars License

🌏 δΈ­ζ–‡ζ–‡ζ‘£

✨ Why FJS? #

  • High Performance - Rust-powered, optimized for mobile platforms
  • ES6 Modules - Full support for import/export syntax
  • Async/Await - Native async JavaScript execution
  • Type Safe - Strongly typed Dart API with sealed classes
  • Bridge Communication - Bidirectional Dart-JS communication
  • Cross Platform - Android, iOS, Linux, macOS, Windows
  • Memory Safe - Built-in GC with configurable limits

🎯 Real-world Usage #

Mikan Flutter - A Flutter client for Mikan Project, an anime subscription and management platform. FJS powers its core JavaScript execution engine.

πŸ“¦ Installation #

dependencies:
  fjs: any

πŸš€ Quick Start #

import 'package:fjs/fjs.dart';

void main() async {
  await LibFjs.init();

  // Create runtime with builtin modules
  final runtime = await JsAsyncRuntime.withOptions(
    builtin: JsBuiltinOptions(
      console: true,
      fetch: true,
      timers: true,
    ),
  );

  // Create context
  final context = await JsAsyncContext.from(runtime: runtime);

  // Create engine
  final engine = JsEngine(context: context);
  await engine.init(bridge: (jsValue) {
    return JsResult.ok(JsValue.string('Hello from Dart'));
  });

  // Execute JavaScript
  final result = await engine.eval(source: JsCode.code('''
    console.log('Hello from FJS!');
    1 + 2
  '''));
  print(result.value); // 3

  await engine.dispose();
}

πŸ—οΈ Runtime & Context APIs #

// Create an async runtime with web-style builtins and one extra ES module.
final runtime = await JsAsyncRuntime.withOptions(
  builtin: JsBuiltinOptions.web(),
  additional: [
    JsModule.code(
      module: 'app/math',
      code: 'export function add(a, b) { return a + b; }',
    ),
  ],
);

// Apply runtime-level safety and diagnostic limits.
await runtime.setInfo(info: 'main-runtime');
await runtime.setMemoryLimit(limit: BigInt.from(64 * 1024 * 1024));
await runtime.setGcThreshold(threshold: BigInt.from(8 * 1024 * 1024));
await runtime.setMaxStackSize(limit: BigInt.from(512 * 1024));

// Create a context from that runtime.
final context = await JsAsyncContext.from(runtime: runtime);

// Evaluate a simple expression and read the structured JsResult.
final evalResult = await context.eval(code: '21 + 21');
print(evalResult.ok.value); // 42

// Enable top-level await / Promise handling explicitly.
final asyncResult = await context.evalWithOptions(
  code: 'await Promise.resolve(40 + 2)',
  options: JsEvalOptions.withPromise(),
);
print(asyncResult.ok.value); // 42

// Load code from disk.
final fileResult = await context.evalFile(path: '/absolute/path/to/script.js');
final strictFileResult = await context.evalFileWithOptions(
  path: '/absolute/path/to/script.js',
  options: JsEvalOptions.defaults(),
);

// Call an exported function from a module that is already available in the runtime.
final functionResult = await context.evalFunction(
  module: 'app/math',
  method: 'add',
  params: [JsValue.integer(2), JsValue.integer(3)],
);
print(functionResult.ok.value); // 5

// Inspect which modules the context can currently import.
final availableModules = await context.getAvailableModules();
print(availableModules);

// Advance or fully drain pending async work when you need explicit control.
if (await runtime.isJobPending()) {
  await runtime.executePendingJob();
}
await runtime.idle();

Low-level context APIs return JsResult, which is useful when you want structured success or error handling instead of exceptions.

Synchronous Runtime & Context #

// Build a synchronous runtime when you do not need async JavaScript execution.
final runtime = await JsRuntime.withOptions(
  builtin: JsBuiltinOptions.essential(),
);
final context = JsContext.from(runtime: runtime);

// Sync contexts return JsResult directly.
final result = context.eval(code: '6 * 7');
print(result.ok.value); // 42

// Apply eval flags such as strict mode.
final strictResult = context.evalWithOptions(
  code: '"use strict"; 8 * 8',
  options: JsEvalOptions.defaults(),
);
print(strictResult.ok.value); // 64

// File-based sync evaluation uses the same JsResult shape.
final fileResult = context.evalFile(path: '/absolute/path/to/script.js');
final fileWithOptions = context.evalFileWithOptions(
  path: '/absolute/path/to/script.js',
  options: JsEvalOptions.defaults(),
);

// Introspect the modules visible to this context.
final modules = context.getAvailableModules();
print(modules);

// Pump the QuickJS job queue manually in sync mode.
while (runtime.isJobPending()) {
  runtime.executePendingJob();
}

// Configure runtime limits and collect memory statistics.
runtime.setMemoryLimit(limit: BigInt.from(32 * 1024 * 1024));
runtime.setGcThreshold(threshold: BigInt.from(4 * 1024 * 1024));
runtime.setMaxStackSize(limit: BigInt.from(256 * 1024));
runtime.setInfo(info: 'sync-runtime');
print(runtime.memoryUsage().summary());
runtime.runGc();

🧱 Source Inputs & Eval Options #

import 'dart:convert';
import 'dart:typed_data';

// Source code can come from a string, a file path, or UTF-8 bytes.
final inlineCode = JsCode.code('1 + 1');
final fileCode = JsCode.path('/absolute/path/to/script.js');
final bytesCode = JsCode.bytes(Uint8List.fromList(utf8.encode('2 + 2')));

// Modules support the same three source forms.
final inlineModule = JsModule.code(
  module: 'feature/inline',
  code: 'export const enabled = true;',
);
final fileModule = JsModule.path(
  module: 'feature/file',
  path: '/absolute/path/to/feature.js',
);
final bytesModule = JsModule.bytes(
  module: 'feature/bytes',
  bytes: utf8.encode('export const answer = 42;'),
);

// Eval options control whether code runs as global script, async code, or module-style code.
final defaultEval = JsEvalOptions.defaults();
final asyncEval = JsEvalOptions.withPromise();
final moduleEval = JsEvalOptions.module();

πŸ“¦ ES6 Modules #

// Declare modules
await engine.declareNewModule(
  module: JsModule.code(module: 'math', code: '''
    export const add = (a, b) => a + b;
    export const multiply = (a, b) => a * b;
  '''),
);

// Use modules
await engine.eval(source: JsCode.code('''
  const { add, multiply } = await import('math');
  console.log(add(2, 3));        // 5
  console.log(multiply(4, 5));   // 20
'''));

// Or call an exported function directly without writing an import wrapper yourself.
final sum = await engine.call(
  module: 'math',
  method: 'add',
  params: [JsValue.integer(2), JsValue.integer(3)],
);
print(sum.value); // 5

// Batch-register multiple modules in one request.
await engine.declareNewModules(modules: [
  JsModule.code(
    module: 'numbers/double',
    code: 'export const double = (value) => value * 2;',
  ),
  JsModule.code(
    module: 'numbers/triple',
    code: 'export const triple = (value) => value * 3;',
  ),
]);

// Execute a module immediately and leave it cached in the current context.
await engine.evaluateModule(
  module: JsModule.code(
    module: 'startup',
    code: 'globalThis.started = true; export default "ready";',
  ),
);

// Inspect the dynamic modules declared on this engine.
final declaredModules = await engine.getDeclaredModules();
final hasMath = await engine.isModuleDeclared(moduleName: 'math');
print(declaredModules);
print(hasMath); // true

Dynamic modules can be cleared only before they are loaded. After a module has been imported or evaluated in a context, recreate the context to replace it.

πŸ“š Module Inventory #

final modules = await engine.getAvailableModules();
print(modules);

final hasConsole = await engine.isModuleAvailable(moduleName: 'console');
final hasXml = await engine.isModuleAvailable(moduleName: 'llrt:xml');
print('console: $hasConsole, llrt:xml: $hasXml');

πŸ”„ Engine Lifecycle Notes #

  • JsEngine wraps an existing JsAsyncContext; disposing the engine does not dispose the underlying context or runtime
  • dispose() detaches the fjs bridge object, drains pending runtime work, and then runs GC before the engine becomes unusable
  • clearPendingModules() only removes dynamic modules that have not been loaded into the current context yet
  • declareNewModules() and declareNewBytecodeModules() reject duplicate module names in a single request

πŸ“¦ Module Bytecode #

// Compile an ES module into QuickJS bytecode without touching the current engine.
final bytecode = await JsBytecode.compile(
  module: JsModule.code(
    module: 'plugin/main.js',
    code: 'export function run() { return "ready"; }',
  ),
  options: JsModuleBytecodeOptions.defaults(),
);

// Validate the bytecode payload before declaring it.
await JsBytecode.validate(module: bytecode);

// Register the precompiled module on the engine.
await engine.declareNewBytecodeModule(module: bytecode);

// Import and execute the declared module like any other ES module.
final result = await engine.eval(source: JsCode.code('''
  const { run } = await import('plugin/main.js');
  run();
'''));

// Reconstruct bytecode from persisted bytes when loading from storage.
final restored = JsModuleBytecode(
  name: bytecode.name,
  bytes: bytecode.bytes,
);
JsBytecode.validateSync(module: restored);

JsBytecode.compile() runs in an isolated QuickJS context, so compiling does not declare or cache the module inside the current engine. JsBytecode.validate() only checks structural validity and embedded module name; it does not execute the module. compileSync() / validateSync() are also available for synchronous callers, but the async variants are safer on the main isolate.

QuickJS bytecode is version-specific and must be treated as trusted input. Recompile bytecode whenever the embedded QuickJS version changes.

Bytecode Bundles #

// Compile a full module graph into one distributable bundle.
final bundle = await JsBytecode.compileModuleBundle(
  entry: 'plugins/main.js',
  modules: [
    JsModule.code(
      module: 'plugins/deps/math.js',
      code: 'export const double = (value) => value * 2;',
    ),
    JsModule.code(
      module: 'plugins/main.js',
      code: '''
        import { double } from './deps/math.js';
        export default { ready: true, answer: double(21) };
      ''',
    ),
  ],
);

// Validate the bundle structure before loading it.
await JsBytecode.validateBundle(bundle: bundle);

// Execute the bundle entry and cache the involved modules.
await engine.evaluateBytecodeBundle(bundle: bundle);

// Read exports by importing the entry module afterwards.
final result = await engine.eval(source: JsCode.code('''
  const { default: plugin } = await import('plugins/main.js');
  plugin
'''));
print(result.value); // { ready: true, answer: 42 }

Bundles are useful when a plugin ships as a module graph instead of a single file. Relative imports are preserved inside the compiled bundle, and declareNewBytecodeBundle() is available when you want to register the bundle without executing its entry yet. validateBundle() is structural: it checks entry presence, duplicate names, and that each payload is readable by the embedded QuickJS version. evaluateBytecodeBundle() executes the entry and populates the module cache; import the entry afterwards to read its exports.

Classic Script Bytecode #

// Compile classic script source into non-module bytecode.
final script = await JsBytecode.compileScript(
  name: 'startup.js',
  source: JsCode.code('''
    await Promise.resolve();
    globalThis.launchCount = (globalThis.launchCount ?? 0) + 1;
    ({ mode: 'script', launchCount: globalThis.launchCount })
  '''),
  options: const JsScriptBytecodeOptions(
    promise: true,
    strict: true,
    stripSource: true,
    stripDebug: true,
    endianness: JsBytecodeEndianness.little,
  ),
);

// Validate before evaluation.
await JsBytecode.validateScript(script: script);

// Execute the script bytecode and read its completion value.
final result = await engine.evaluateScriptBytecode(script: script);
print(result.value); // { mode: 'script', launchCount: 1 }

Script bytecode is the non-module counterpart to ES module bytecode. validateScript() is structural only: it ensures the bytes decode as executable non-module bytecode under the embedded QuickJS version. QuickJS does not expose an embedded script name to verify at load time, so the name acts as compile-time metadata and the source filename shown in stack traces.

🧾 Values, Results, and Errors #

import 'dart:typed_data';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';

// Convert common Dart values into structured JsValue instances automatically.
final payload = JsValue.from({
  'enabled': true,
  'count': 3,
  'tags': ['a', 'b'],
  'buffer': Uint8List.fromList([1, 2, 3]),
});
print(payload.typeName()); // Object
print(payload.value); // Dart Map<String, dynamic>

// Or build typed JsValue trees yourself when you need exact control.
final typed = JsValue.object({
  'big': JsValue.bigint('9007199254740993'),
  'createdAt': JsValue.date(DateTime.now().millisecondsSinceEpoch),
});
print(typed.value);

// Low-level context APIs return JsResult instead of throwing.
final result = await context.eval(code: '40 + 2');
if (result.isOk) {
  print(result.ok.value); // 42
} else {
  print('${result.err.code()}: ${result.err}');
}

// JsError values are useful for structured error handling and retry decisions.
const syntaxError = JsError.syntax(
  message: 'Unexpected token',
  line: 1,
  column: 10,
);
print(syntaxError.code());
print(syntaxError.isRecoverable());

// High-level execution APIs still throw AnyhowException on failure.
try {
  await engine.eval(source: JsCode.code('invalid.code()'));
} on AnyhowException catch (e) {
  print('Execution failed: ${e.message}');
}

JsError is returned inside JsResult.err(...) for structured bridge and low-level context results. Public execution APIs like eval() and call() currently surface Rust-side failures as AnyhowException.

πŸŒ‰ Bridge Communication #

// The bridge receives a JsValue and returns a JsResult back to JavaScript.
await engine.init(bridge: (jsValue) async {
  final data = jsValue.value;

  if (data is Map && data['action'] == 'fetchUser') {
    final user = await fetchUser(data['id']);
    return JsResult.ok(JsValue.from(user));
  }

  return JsResult.ok(JsValue.none());
});

// In JavaScript, call back into Dart through the injected fjs object.
await engine.eval(source: JsCode.code('''
  const user = await fjs.bridge_call({ action: 'fetchUser', id: 123 });
  console.log(user);
'''));

🧠 Memory Management #

// Set runtime safety limits.
await runtime.setMemoryLimit(limit: BigInt.from(50 * 1024 * 1024)); // 50MB
await runtime.setGcThreshold(threshold: BigInt.from(10 * 1024 * 1024)); // 10MB
await runtime.setMaxStackSize(limit: BigInt.from(512 * 1024)); // 512KB

// Inspect current memory usage.
final usage = await runtime.memoryUsage();
print(usage.summary());

// Force a garbage collection pass when you need immediate cleanup.
await runtime.runGc();

πŸ“š Core API #

JsAsyncRuntime & JsAsyncContext #

abstract class JsAsyncRuntime {
  factory JsAsyncRuntime();
  static Future<JsAsyncRuntime> withOptions({
    JsBuiltinOptions? builtin,
    List<JsModule>? additional,
  });

  Future<bool> isJobPending();
  Future<bool> executePendingJob();
  Future<void> idle();
  Future<MemoryUsage> memoryUsage();
  Future<void> setMemoryLimit({required BigInt limit});
  Future<void> setGcThreshold({required BigInt threshold});
  Future<void> setMaxStackSize({required BigInt limit});
  Future<void> setInfo({required String info});
  Future<void> runGc();
}

abstract class JsAsyncContext {
  static Future<JsAsyncContext> from({required JsAsyncRuntime runtime});

  Future<JsResult> eval({required String code});
  Future<JsResult> evalWithOptions({required String code, required JsEvalOptions options});
  Future<JsResult> evalFile({required String path});
  Future<JsResult> evalFileWithOptions({required String path, required JsEvalOptions options});
  Future<JsResult> evalFunction({
    required String module,
    required String method,
    List<JsValue>? params,
  });

  Future<List<String>> getAvailableModules();
}

JsRuntime & JsContext #

abstract class JsRuntime {
  factory JsRuntime();
  static Future<JsRuntime> withOptions({
    JsBuiltinOptions? builtin,
    List<JsModule>? additional,
  });

  bool isJobPending();
  bool executePendingJob();
  MemoryUsage memoryUsage();
  void setMemoryLimit({required BigInt limit});
  void setGcThreshold({required BigInt threshold});
  void setMaxStackSize({required BigInt limit});
  void setInfo({required String info});
  void runGc();
}

abstract class JsContext {
  static JsContext from({required JsRuntime runtime});

  JsResult eval({required String code});
  JsResult evalWithOptions({required String code, required JsEvalOptions options});
  JsResult evalFile({required String path});
  JsResult evalFileWithOptions({required String path, required JsEvalOptions options});
  List<String> getAvailableModules();
}

JsEngine #

class JsEngine {
  factory JsEngine({required JsAsyncContext context});

  Future<void> init({required FutureOr<JsResult> Function(JsValue) bridge});
  Future<void> initWithoutBridge();
  Future<JsValue> eval({required JsCode source, JsEvalOptions? options});
  Future<JsValue> call({required String module, required String method, List<JsValue>? params});

  Future<void> declareNewModule({required JsModule module});
  Future<void> declareNewModules({required List<JsModule> modules}); // rejects duplicate names in one request
  Future<void> declareNewBytecodeBundle({required JsModuleBytecodeBundle bundle});
  Future<void> declareNewBytecodeModule({required JsModuleBytecode module});
  Future<void> declareNewBytecodeModules({required List<JsModuleBytecode> modules}); // rejects duplicate names in one request
  Future<void> clearPendingModules();
  Future<List<String>> getAvailableModules();
  Future<bool> isModuleDeclared({required String moduleName});
  Future<bool> isModuleAvailable({required String moduleName});
  Future<List<String>> getDeclaredModules();
  Future<JsValue> evaluateBytecodeBundle({required JsModuleBytecodeBundle bundle});
  Future<JsValue> evaluateModule({required JsModule module});
  Future<JsValue> evaluateBytecodeModule({required JsModuleBytecode module});
  Future<JsValue> evaluateScriptBytecode({required JsScriptBytecode script});

  Future<void> dispose(); // drains pending runtime work, then runs GC
  bool get running;
  bool get disposed;
  JsAsyncContext get context; // engine does not own the returned context
}

MemoryUsage #

abstract class MemoryUsage {
  int get totalMemory;
  int get totalAllocations;
  int get mallocSize;
  int get objCount;
  int get strCount;
  String summary();
}

JsValue #

sealed class JsValue {
  const factory JsValue.none();
  const factory JsValue.boolean(bool value);
  const factory JsValue.integer(PlatformInt64 value);
  const factory JsValue.float(double value);
  const factory JsValue.bigint(String value);
  const factory JsValue.string(String value);
  const factory JsValue.bytes(Uint8List value);
  const factory JsValue.array(List<JsValue> value);
  const factory JsValue.object(Map<String, JsValue> value);
  const factory JsValue.date(PlatformInt64 value);
  const factory JsValue.symbol(String value);
  const factory JsValue.function(String value);

  static JsValue from(Object? any);
  String typeName();
  dynamic get value;
}

JsBuiltinOptions & JsEvalOptions #

sealed class JsBuiltinOptions {
  const factory JsBuiltinOptions({
    bool? console,
    bool? fetch,
    bool? timers,
    bool? crypto,
    bool? fs,
    bool? url,
    bool? process,
    bool? path,
    bool? util,
    bool? intl,
    bool? temporal,
    // ... other builtin toggles
  });

  static JsBuiltinOptions none();
  static JsBuiltinOptions essential();
  static JsBuiltinOptions web();
  static JsBuiltinOptions node();
  static JsBuiltinOptions all();
}

sealed class JsEvalOptions {
  factory JsEvalOptions({
    bool? global,
    bool? strict,
    bool? backtraceBarrier,
    bool? promise,
  });

  static JsEvalOptions defaults();
  static JsEvalOptions withPromise();
  static JsEvalOptions module();
}

JsCode, JsModule, and Bytecode #

sealed class JsCode {
  const factory JsCode.code(String value);    // Inline code
  const factory JsCode.path(String value);    // File path
  const factory JsCode.bytes(Uint8List value); // Raw UTF-8 source bytes
}

sealed class JsModule {
  static JsModule code({required String module, required String code});
  static JsModule path({required String module, required String path});
  static JsModule bytes({required String module, required List<int> bytes}); // UTF-8 source bytes
}

sealed class JsModuleBytecode {
  factory JsModuleBytecode({required String name, required List<int> bytes});
}

sealed class JsModuleBytecodeBundle {
  factory JsModuleBytecodeBundle({
    String? entry,
    required List<JsModuleBytecode> modules,
  });
}

sealed class JsScriptBytecode {
  factory JsScriptBytecode({required String name, required List<int> bytes});
}

class JsBytecode {
  static JsModuleBytecode compileSync({
    required JsModule module,
    JsModuleBytecodeOptions? options,
  });

  static Future<JsModuleBytecode> compile({
    required JsModule module,
    JsModuleBytecodeOptions? options,
  });

  static JsModuleBytecodeBundle compileModuleBundleSync({
    required List<JsModule> modules,
    String? entry,
    JsModuleBytecodeOptions? options,
  });

  static Future<JsModuleBytecodeBundle> compileModuleBundle({
    required List<JsModule> modules,
    String? entry,
    JsModuleBytecodeOptions? options,
  });

  static JsScriptBytecode compileScriptSync({
    required String name,
    required JsCode source,
    JsScriptBytecodeOptions? options,
  });

  static Future<JsScriptBytecode> compileScript({
    required String name,
    required JsCode source,
    JsScriptBytecodeOptions? options,
  });

  static void validateSync({required JsModuleBytecode module});
  static Future<void> validate({required JsModuleBytecode module});
  static void validateBundleSync({required JsModuleBytecodeBundle bundle});
  static Future<void> validateBundle({required JsModuleBytecodeBundle bundle});
  static void validateScriptSync({required JsScriptBytecode script});
  static Future<void> validateScript({required JsScriptBytecode script});
}

sealed class JsModuleBytecodeOptions {
  const factory JsModuleBytecodeOptions({
    JsBytecodeEndianness? endianness,
    bool? stripSource,
    bool? stripDebug,
  });

  static JsModuleBytecodeOptions defaults();
}

sealed class JsScriptBytecodeOptions {
  const factory JsScriptBytecodeOptions({
    JsBytecodeEndianness? endianness,
    bool? stripSource,
    bool? stripDebug,
    bool? strict,
    bool? backtraceBarrier,
    bool? promise,
  });

  static JsScriptBytecodeOptions defaults();
}

JsResult & JsError #

sealed class JsResult {
  const factory JsResult.ok(JsValue value);
  const factory JsResult.err(JsError error);

  bool get isOk;
  bool get isErr;
  JsValue get ok;
  JsError get err;
}

sealed class JsError {
  const factory JsError.promise(String message);
  const factory JsError.module({String? module, String? method, required String message});
  const factory JsError.context(String message);
  const factory JsError.storage(String message);
  const factory JsError.io({String? path, required String message});
  const factory JsError.runtime(String message);
  const factory JsError.generic(String message);
  const factory JsError.engine(String message);
  const factory JsError.bridge(String message);
  const factory JsError.conversion({
    required String from,
    required String to,
    required String message,
  });
  const factory JsError.timeout({
    required String operation,
    required BigInt timeoutMs,
  });
  const factory JsError.memoryLimit({
    required BigInt current,
    required BigInt limit,
  });
  const factory JsError.stackOverflow(String message);
  const factory JsError.syntax({int? line, int? column, required String message});
  const factory JsError.reference(String message);
  const factory JsError.type(String message);
  const factory JsError.cancelled(String message);

  String code();
  bool isRecoverable();
}

🧩 Built-in Runtime Features #

Some builtin options expose importable modules, and some install globals directly on the runtime.

Option Description
abort AbortController and abort-related globals
assert Assertion helpers
asyncHooks Async lifecycle tracking
buffer Buffer utilities for binary data
childProcess Child process spawning
console Console logging (console.log, console.error, etc.)
crypto Cryptographic functions and Web Crypto globals
dgram UDP sockets
dns DNS resolution
events EventEmitter support
exceptions Exception helpers installed globally
fetch Fetch API globals
fs File system operations
https HTTPS client module
intl Lightweight Intl.DateTimeFormat timezone support
navigator Navigator globals
net TCP sockets
os Operating system utilities (not available on iOS)
path Path manipulation (POSIX/Windows)
perfHooks Performance measurement APIs
process Process information and environment
streamWeb Web Streams API
stringDecoder String decoding from buffers
temporal Temporal global
timers Timer functions (setTimeout, setInterval, setImmediate)
tty Terminal utilities
url URL parsing and formatting
util Utility functions
zlib Compression/decompression (gzip, deflate)
json JSON static method compatibility helpers

Quick Presets #

// Essential: console, timers, buffer, util, json
JsBuiltinOptions.essential()

// Web: console, timers, fetch, url, crypto, streamWeb, navigator, exceptions, intl, json
JsBuiltinOptions.web()

// Node.js: Most Node-compatible modules, plus https and intl
JsBuiltinOptions.node()

// All modules
JsBuiltinOptions.all()

// Custom selection
JsBuiltinOptions(
  console: true,
  fetch: true,
  timers: true,
  // ... other options
)

⚑ Performance Tips #

  1. Reuse Engines - Create once, use many times
  2. Set Memory Limits - Configure appropriate limits
  3. Use Source Bytes - Prefer JsCode.bytes() / JsModule.bytes() when your JavaScript source is already in UTF-8 bytes
  4. Batch Operations - Group related operations

πŸ“„ License #

MIT License - see LICENSE file.

16
likes
150
points
359
downloads

Documentation

API reference

Publisher

verified publisherfluttercandies.com

Weekly Downloads

A high-performance JavaScript runtime for Flutter applications, built with Rust and powered by QuickJS.

Repository (GitHub)
View/report issues

Topics

#quickjs #js-engine #js-runtime #javascript #runtime

License

MIT (license)

Dependencies

flutter, flutter_rust_bridge, freezed_annotation, plugin_platform_interface

More

Packages that depend on fjs

Packages that implement fjs