---
title: "Oden: The Server-First, JavaScript-esque Runtime"
created: "2026-04-23T23:46:23.279Z"
label: "RFC5"
author: "Thomas Hunter II"
status: "review"
visibility: "public"
summary: "This document proposes the initial version of **Oden** (an anagram of
  Node and Deno). It runs \"JavaScript\" on a server without concern for
  compatibility with Browsers or existing libraries. It's a fork of both Node.js
  and V8."
image: null
tags: []
watchers: []
reviewers: []
links: []
---

This document proposes the initial version of **Oden** (an anagram of Node and Deno). It is intended to be a way to run "JavaScript" on a server but with the following caveats:

- Only JavaScript features that are beneficial in a server context are retained
- Many existing JavaScript APIs and globals are locked behind `require()` calls
- Compatability with existing scripts and libraries is not a consideration
- This is a fork of Node.js (via modifications to Node.js APIs)
- This is also a fork of JavaScript (via modifications to V8)

Another way to think about this project is what if `node`, the binary for interpreting scripts and running backend services, had full control of the language being interpreted, similar to `python` and `php`?

This is not a complete set of changes for defining how Oden will work but is instead an initial pass.

# Further Reading

The following documents have more information about the background behind Oden:

- [Server-Side JavaScript Arc Part 1: A Retrospective on the Golden Age of Node.js](https://thomashunter.name/posts/2026-04-24-server-side-js-p1-golden-age-of-nodejs)
- [Server-Side JavaScript Arc Part 2: A Server-First JavaScript Runtime](https://thomashunter.name/posts/2026-04-24-server-side-js-p2-server-first-js)
- [Oden.js gist: Try a simplified version of Oden using the Node.js REPL](https://gist.github.com/tlhunter/abf4590f85f5b1267c00a8be4317b73a)

# Node.js API Modifications

Based on the number of modifications required this would need to be implemented as a "hard fork" of Node.js. I don't think it would be feasable to float patches on top of Node.js.

## New Internal Modules

### Errors

The following JavaScript globals are moved to top level exports of a new `require('errors')` module:

```js
// before
global.EvalError;
global.URIError;
global.AggregateError;
global.DOMException;

// after
const {
  EvalError,
  URIError,
  AggregateError,
  DOMException,
  Error, // also remains a global for convenience
  TypeError, // also remains a global for convenience
  SyntaxError, // also remains a global for convenience
  RangeError, // also remains a global for convenience
  ReferenceError // also remains a global for convenience
} = require('errors');
```

### Math

The following JavaScript global is made the top level export of a new `require('math')` module and is removed from global:

```js
// before
global.Math;

// after
const Math = require('math');
```

### Threads

The following JavaScript globals are moved to top level exports of a new `require('threads')` module:

```js
// before
global.BroadcastChannel;
global.Atomics;
global.MessagePort;
global.MessageEvent;
global.MessageChannel;
global.SharedArrayBuffer;

// after
const {
  BroadcastChannel,
  Atomics,
  MessagePort,
  MessageEvent,
  MessageChannel,
  SharedArrayBuffer
} = require('threads');
```

### Array Buffer

The following JavaScript globals are moved to top level exports of a new `require('array_buffer')` module:

```js
// before
global.SharedArrayBuffer;
global.ArrayBuffer;
global.BigInt64Array;
global.BigUint64Array;
global.DataView;
global.Float32Array;
global.Float64Array;
global.Int16Array;
global.Int32Array;
global.Int8Array;
global.Uint16Array;
global.Uint32Array;
global.Uint8Array;
global.Uint8ClampedArray;
global.Blob;

// after
const {
  SharedArrayBuffer, // redundant with require('threads').SharedArrayBuffer
  ArrayBuffer,
  TypedArray, // currently not directly available in JavaScript
  BigInt64Array,
  BigUint64Array,
  DataView,
  Float32Array,
  Float64Array,
  Int16Array,
  Int32Array,
  Int8Array,
  Uint16Array,
  Uint32Array,
  Uint8Array,
  Uint8ClampedArray,
  Blob
} = require('array_buffer');
```

### Text Encoder

The following JavaScript globals are moved to top level exports of a new `require('text_encoder')` module:

```js
// before
global.TextEncoder;
global.TextEncoderStream;
global.TextDecoder;
global.TextDecoderStream;

// after
const {
  TextEncoder,
  TextEncoderStream,
  TextDecoder,
  TextDecoderStream
} = require('text_encoder');
```

### Intl

The following JavaScript globals are moved to top level exports of a new `require('intl')` module:

```js
// before
global.Intl;

// after
const Intl = require('intl');
```

### Reflection

The following JavaScript globals are moved to top level exports of a new `require('reflection')` module:

```js
// before
global.Reflect;
global.Proxy;

// after
const {
  Reflect,
  Proxy
} = require('reflection');
```

### Navigator

The following JavaScript globals are moved to top level exports of a new `require('navigator')` module:

```js
// before
global.navigator;
global.Navigator;

// after
const {
  navigator,
  Navigator
} = require('navigator');
```

### Finalization Registry

The following JavaScript globals are moved to top level exports of a new `require('finalization_registry')` module:

```js
// before
global.FinalizationRegistry;

// after
const FinalizationRegistry = require('finalization_registry');
```

### Date Time

The following JavaScript globals are moved to top level exports of a new `require('datetime')` module:

```js
// before
global.Date;
global.Temporal;

// after
const {
  Date,
  Temporal
} = require('datetime');
```

## Mutated Existing Modules

The following modules are extended by moving existing globals to the list of exports:

### Timers

```js
// before
global.queueMicrotask;
const sleep = ms => new Promise((resolve) => timers.setTimeout(resolve, ms));

// after
const {
  queueMicrotask,
  sleep
} = require('timers');
```

### Events

The following JavaScript globals are moved to top level exports of the `require('events')` object:

```js
// before
global.EventTarget;
global.Event;
global.CloseEvent;
global.CustomEvent;

// after
const {
  EventTarget,
  Event,
  CloseEvent,
  CustomEvent
} = require('events');
```

### Crypto

The following JavaScript globals are moved to top level exports of the `require('crypto')` object:

```js
// before
global.Crypto;
global.CryptoKey;
global.SubtleCrypto;

// after
const {
  Crypto,
  CryptoKey,
  SubtleCrypto
} = require('crypto');
```

### File

The following JavaScript globals are moved to top level exports of the `require('fs')` object:

```js
// before
global.File;

// after
const {
  File
} = require('fs');
```

### HTTP

The following JavaScript globals are moved to top level exports of the `require('http')` object:

```js
// before
global.fetch;
global.FormData;
global.Request;
global.Response;
global.Headers;
global.AbortController;
global.AbortSignal;
global.WebSocket;

// after
const {
  fetch,
  FormData,
  Request,
  Response,
  Headers,
  AbortController,
  AbortSignal,
  WebSocket
} = require('http');
```

### URL

The following JavaScript globals are moved to top level exports of the `require('url')` object:

```js
// before
global.encodeURI;
global.decodeURI;
global.encodeURIComponent;
global.escape;
global.unescape;
global.decodeURIComponent;
global.URL; // already a part of require('url') but removed from global
global.URLSearchParams; // already a part of require('url') but removed from global
global.URLPattern; // already a part of require('url') but removed from global

// after
const {
  encodeURI,
  decodeURI,
  encodeURIComponent,
  escape,
  unescape,
  decodeURIComponent
} = require('url');
```

### Stream

The following JavaScript globals are moved to top level exports of the `require('stream')` object:

```js
// before
global.CompressionStream;
global.DecompressionStream;
global.ReadableByteStreamController;
global.ReadableStream;
global.ReadableStreamBYOBReader;
global.ReadableStreamBYOBRequest;
global.ReadableStreamDefaultController;
global.ReadableStreamDefaultReader;
global.TransformStream;
global.TransformStreamDefaultController;
global.WritableStream;
global.WritableStreamDefaultController;
global.WritableStreamDefaultWriter;
global.ByteLengthQueuingStrategy;
global.CountQueuingStrategy;

// after
const {
  CompressionStream,
  DecompressionStream,
  ReadableByteStreamController,
  ReadableStream,
  ReadableStreamBYOBReader,
  ReadableStreamBYOBRequest,
  ReadableStreamDefaultController,
  ReadableStreamDefaultReader,
  TransformStream,
  TransformStreamDefaultController,
  WritableStream,
  WritableStreamDefaultController,
  WritableStreamDefaultWriter,
  ByteLengthQueuingStrategy,
  CountQueuingStrategy
} = require('stream');
```

## Other Removed Globals

The following globals are removed as they already exist in an internal module:

```js
// redundant with require('process');
global.process;

// redundant with require('timers');
global.setTimeout;
global.clearTimeout;
global.setImmediate;
global.clearImmediate;
global.setInterval;
global.clearInterval;

// redundant with require('perf_hooks');
global.performance;
global.Performance;
global.PerformanceEntry;
global.PerformanceMark;
global.PerformanceMeasure;
global.PerformanceObserver;
global.PerformanceObserverEntryList;
global.PerformanceResourceTiming;
```

## Dropped Functionality

- The `Buffer` class is removed entirely in favor of `TypedArray`.
- Either completely remove `require()` or completely remove `import`.
  - Exactly one of CJS or ESM is ideal.

# JavaScript & V8 Modifications

I don't know much about V8 internals but I think it would also need to be a "hard fork" of V8 versus a floating patch.

## Modified Functionality

### `typeof` Operator

Calling `typeof null` now returns a string value of `'null'`.

### Object Stringification

Generally, when objects are stringified they will be converted to JSON:

```js
Object.prototype.toString = function(...args) { return JSON.stringify(this, ...args); };
Set.prototype.toString = function() { return JSON.stringify(Array.from(this)); };
Map.prototype.toString = function() { return JSON.stringify(Object.fromEntries(this)); };
Date.prototype.toString = Date.prototype.toISOString;
```

## Dropped Functionality

### String Prototype Methods

The following string prototype methods are removed:

```js
// before
String.prototype.anchor;
String.prototype.big;
String.prototype.blink;
String.prototype.bold;
String.prototype.fixed;
String.prototype.fontcolor;
String.prototype.fontsize;
String.prototype.italics;
String.prototype.link;
String.prototype.small;
String.prototype.strike;
String.prototype.sub;
String.prototype.sup;
```

### `btoa` and `atob`

The `btoa` and `atob` global functions are removed however the functionality still exists as string methods:

```js
// before
global.btoa;
global.atob;

// after
String.prototype.toBase64 = function() { return _btoa(this); };
String.prototype.fromBase64 = function() { return _atob(this); };
```

### URL Methods

Several global URL functions are removed and made into string methods. Note that these removed functions are moved to the `require('url')` internal module, as mentioned above.

```js
// before
global.encodeURI;
global.decodeURI;
global.escape;
global.unescape;
global.decodeURIComponent;

// after
String.prototype.encodeURI = function() { return global.encodeURI(this); };
String.prototype.decodeURI = function() { return global.decodeURI(this); };
String.prototype.escapeURI = function() { return global.escape(this); };
String.prototype.unescapeURI = function() { return global.unescape(this); };
String.prototype.decodeURIComponent = function() { return global.decodeURIComponent(this);
```

### New String Methods

The following additional String prototype methods are added:

```js
// convert a JSON string into a new object:
String.prototype.fromJson = function() { return JSON.parse(this); };

// create an array buffer from a string:
String.prototype.toArrayBuffer = function() { return (new _encoder()).encode(this); };
```