This document defines a project titled Oden.js (an anagram of Node and Deno). It is intended to be a way to run "JavaScript" on a server but with the following:
- Only JavaScript features that are beneficial in a server context are retained
- Many existing features are hidden behind
require()calls - Compatability with existing scripts and libraries is not a consideration
- This is essentially a fork of JavaScript (via V8)
- This is essentially a fork of Node.js (by modifying APIs)
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. Instead it's more of an initial pass. Once implemented and a oden binary is generated it can then be shared and tested.
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:
// before
global.EvalError;
global.URIError;
global.AggregateError;
global.DOMException;
// after
const {
EvalError,
URIError,
AggregateError,
DOMException
} = require('errors');
The following remain as globals but are also exports of require('errors'):
global.Error;
global.TypeError;
global.SyntaxError;
global.RangeError;
global.ReferenceError;
Math #
The following JavaScript global is made the top level export of a new require('math') module and is removed from global:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// before
global.Date;
global.Temporal;
// after
const {
Date,
Temporal
} = require('datetime');
Mutated Existing Modules #
The following modules are modified and existing globals are added there:
Timers #
const times = require('timers');
timers.queueMicrotask = global.queueMicrotask;
delete global.queueMicrotask;
timers.sleep = ms => new Promise((resolve) => timers.setTimeout(resolve, ms));
Events #
The following JavaScript globals are moved to top level exports of the require('events') object:
global.EventTarget;
global.Event;
global.CloseEvent;
global.CustomEvent;
Crypto #
The following JavaScript globals are moved to top level exports of the require('crypto') object:
global.Crypto;
global.CryptoKey;
global.SubtleCrypto;
File #
The following JavaScript globals are moved to top level exports of the require('file') object:
global.File;
HTTP #
The following JavaScript globals are moved to top level exports of the require('http') object:
global.fetch;
global.FormData;
global.Request;
global.Response;
global.Headers;
global.AbortController;
global.AbortSignal;
global.WebSocket;
URL #
The following JavaScript globals are moved to top level exports of the require('url') object:
global.encodeURI;
global.decodeURI;
global.encodeURIComponent;
global.escape;
global.unescape;
global.decodeURIComponent;
The following JavaScript globals are already on the top level exports of the require('url') object and are therefor simply removed from the global:
delete global.URL;
delete global.URLSearchParams;
delete global.URLPattern;
Stream #
The following JavaScript globals are moved to top level exports of the require('stream') object:
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;
Removed Globals #
The following globals are removed as they already exist in an internal module:
// 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
Bufferclass is removed entirely in favor ofTypedArray. - Either completely remove
require()or completely removeimport. One of CJS or ESM is fine.
JavaScript & V8 Modifications #
I don't know much about V8 but I think it would also need to be a "hard fork" of V8.
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:
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:
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:
const _btoa = global.btoa;
const _atob = global.atob;
String.prototype.toBase64 = function() { return _btoa(this); };
String.prototype.fromBase64 = function() { return _atob(this); };
delete global.btoa;
delete global.atob;
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.
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);
Added String Methods #
The following additional String prototype methods are added:
// 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); };
History Log
Complete History Log| Operation | Instigator | Revision | When |
|---|---|---|---|
| New RFC revision created: 13 | Thomas Hunter II | r13 | |
| New RFC revision created: 12 | Thomas Hunter II | r12 | |
| New RFC revision created: 11 | Thomas Hunter II | r11 | |
| New RFC revision created: 10 | Thomas Hunter II | r10 | |
| New RFC revision created: 9 | Thomas Hunter II | r9 | |
| New RFC revision created: 8 | Thomas Hunter II | r8 | |
| RFC visibility changed from Internal to Public | Thomas Hunter II | r7 | |
| New RFC revision created: 7 | Thomas Hunter II | r7 | |
| RFC status changed from draft to review | Thomas Hunter II | r6 | |
| New RFC revision created: 6 | Thomas Hunter II | r6 |
RFC Revision: