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 limitations:
- Only JavaScript features that are beneficial in a server context are retained
- Many existing features are hidden behind
require()calls - This is essentially a fork of JavaScript (via V8)
- This is essentially a fork of Node.js (by modifying APIs)
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:
global.EvalError;
global.URIError;
global.AggregateError;
global.DOMException;
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:
global.Math;
Threads #
The following JavaScript globals are moved to top level exports of a new require('threads') module:
global.BroadcastChannel;
global.Atomics;
global.MessagePort;
global.MessageEvent;
global.MessageChannel;
Atomics #
The following JavaScript globals are moved to top level exports of a new require('atomics') module:
global.Atomics;
global.SharedArrayBuffer;
TODO: Should this be rolled into require('threads') instead?
Array Buffer #
The following JavaScript globals are moved to top level exports of a new require('array_buffer') module:
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;
An additional export is added as well, notably the TypedArray class, which is the prototype of the aforementioned classes:
require('array_buffer').TypedArray = global.Int8Array.prototype;
Text Encoder #
The following JavaScript globals are moved to top level exports of a new require('text_encoder') module:
global.TextEncoder;
global.TextEncoderStream;
global.TextDecoder;
global.TextDecoderStream;
Intl #
The following JavaScript globals are moved to top level exports of a new require('intl') module:
global.Intl;
Reflection #
The following JavaScript globals are moved to top level exports of a new require('reflection') module:
global.Reflect;
global.Proxy;
Navigator #
The following JavaScript globals are moved to top level exports of a new require('navigator') module:
global.navigator;
global.Navigator;
TODO: Is this stuff important in a pure server context?
Finalization Registry #
The following JavaScript globals are moved to top level exports of a new require('finalization_registry') module:
global.FinalizationRegistry;
Date Time #
The following JavaScript globals are moved to top level exports of a new require('datetime') module:
global.Date;
global.Temporal;
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: