diff --git a/content/2020-02-11-enterprise-update.md b/content/2020-02-11-enterprise-update.md deleted file mode 100644 index 8b60375..0000000 --- a/content/2020-02-11-enterprise-update.md +++ /dev/null @@ -1,14 +0,0 @@ -+++ -title = "enterprise: a new ui framework" -date = 2020-02-11 -draft = true - -[taxonomies] -tags = [] -+++ - -This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the ideation and the implementation process. - -## Goals of the project - -[1]: https://remexre.xyz diff --git a/content/enterprise/prototype/helloworld.js b/content/enterprise/prototype/helloworld.js new file mode 100644 index 0000000..5e8346e --- /dev/null +++ b/content/enterprise/prototype/helloworld.js @@ -0,0 +1,639 @@ +"use strict"; + +if( typeof Rust === "undefined" ) { + var Rust = {}; +} + +(function( root, factory ) { + if( typeof define === "function" && define.amd ) { + define( [], factory ); + } else if( typeof module === "object" && module.exports ) { + module.exports = factory(); + } else { + Rust.helloworld = factory(); + } +}( this, function() { + return (function( module_factory ) { + var instance = module_factory(); + + if( typeof process === "object" && typeof process.versions === "object" && typeof process.versions.node === "string" ) { + var fs = require( "fs" ); + var path = require( "path" ); + var wasm_path = path.join( __dirname, "helloworld.wasm" ); + var buffer = fs.readFileSync( wasm_path ); + var mod = new WebAssembly.Module( buffer ); + var wasm_instance = new WebAssembly.Instance( mod, instance.imports ); + return instance.initialize( wasm_instance ); + } else { + var file = fetch( "helloworld.wasm", {credentials: "same-origin"} ); + + var wasm_instance = ( typeof WebAssembly.instantiateStreaming === "function" + ? WebAssembly.instantiateStreaming( file, instance.imports ) + .then( function( result ) { return result.instance; } ) + + : file + .then( function( response ) { return response.arrayBuffer(); } ) + .then( function( bytes ) { return WebAssembly.compile( bytes ); } ) + .then( function( mod ) { return WebAssembly.instantiate( mod, instance.imports ) } ) ); + + return wasm_instance + .then( function( wasm_instance ) { + var exports = instance.initialize( wasm_instance ); + console.log( "Finished loading Rust wasm module 'helloworld'" ); + return exports; + }) + .catch( function( error ) { + console.log( "Error loading Rust wasm module 'helloworld':", error ); + throw error; + }); + } + }( function() { + var Module = {}; + + Module.STDWEB_PRIVATE = {}; + +// This is based on code from Emscripten's preamble.js. +Module.STDWEB_PRIVATE.to_utf8 = function to_utf8( str, addr ) { + var HEAPU8 = Module.HEAPU8; + for( var i = 0; i < str.length; ++i ) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt( i ); // possibly a lead surrogate + if( u >= 0xD800 && u <= 0xDFFF ) { + u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF); + } + + if( u <= 0x7F ) { + HEAPU8[ addr++ ] = u; + } else if( u <= 0x7FF ) { + HEAPU8[ addr++ ] = 0xC0 | (u >> 6); + HEAPU8[ addr++ ] = 0x80 | (u & 63); + } else if( u <= 0xFFFF ) { + HEAPU8[ addr++ ] = 0xE0 | (u >> 12); + HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); + HEAPU8[ addr++ ] = 0x80 | (u & 63); + } else if( u <= 0x1FFFFF ) { + HEAPU8[ addr++ ] = 0xF0 | (u >> 18); + HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63); + HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); + HEAPU8[ addr++ ] = 0x80 | (u & 63); + } else if( u <= 0x3FFFFFF ) { + HEAPU8[ addr++ ] = 0xF8 | (u >> 24); + HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63); + HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63); + HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); + HEAPU8[ addr++ ] = 0x80 | (u & 63); + } else { + HEAPU8[ addr++ ] = 0xFC | (u >> 30); + HEAPU8[ addr++ ] = 0x80 | ((u >> 24) & 63); + HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63); + HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63); + HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63); + HEAPU8[ addr++ ] = 0x80 | (u & 63); + } + } +}; + +Module.STDWEB_PRIVATE.noop = function() {}; +Module.STDWEB_PRIVATE.to_js = function to_js( address ) { + var kind = Module.HEAPU8[ address + 12 ]; + if( kind === 0 ) { + return undefined; + } else if( kind === 1 ) { + return null; + } else if( kind === 2 ) { + return Module.HEAP32[ address / 4 ]; + } else if( kind === 3 ) { + return Module.HEAPF64[ address / 8 ]; + } else if( kind === 4 ) { + var pointer = Module.HEAPU32[ address / 4 ]; + var length = Module.HEAPU32[ (address + 4) / 4 ]; + return Module.STDWEB_PRIVATE.to_js_string( pointer, length ); + } else if( kind === 5 ) { + return false; + } else if( kind === 6 ) { + return true; + } else if( kind === 7 ) { + var pointer = Module.STDWEB_PRIVATE.arena + Module.HEAPU32[ address / 4 ]; + var length = Module.HEAPU32[ (address + 4) / 4 ]; + var output = []; + for( var i = 0; i < length; ++i ) { + output.push( Module.STDWEB_PRIVATE.to_js( pointer + i * 16 ) ); + } + return output; + } else if( kind === 8 ) { + var arena = Module.STDWEB_PRIVATE.arena; + var value_array_pointer = arena + Module.HEAPU32[ address / 4 ]; + var length = Module.HEAPU32[ (address + 4) / 4 ]; + var key_array_pointer = arena + Module.HEAPU32[ (address + 8) / 4 ]; + var output = {}; + for( var i = 0; i < length; ++i ) { + var key_pointer = Module.HEAPU32[ (key_array_pointer + i * 8) / 4 ]; + var key_length = Module.HEAPU32[ (key_array_pointer + 4 + i * 8) / 4 ]; + var key = Module.STDWEB_PRIVATE.to_js_string( key_pointer, key_length ); + var value = Module.STDWEB_PRIVATE.to_js( value_array_pointer + i * 16 ); + output[ key ] = value; + } + return output; + } else if( kind === 9 ) { + return Module.STDWEB_PRIVATE.acquire_js_reference( Module.HEAP32[ address / 4 ] ); + } else if( kind === 10 || kind === 12 || kind === 13 ) { + var adapter_pointer = Module.HEAPU32[ address / 4 ]; + var pointer = Module.HEAPU32[ (address + 4) / 4 ]; + var deallocator_pointer = Module.HEAPU32[ (address + 8) / 4 ]; + var num_ongoing_calls = 0; + var drop_queued = false; + var output = function() { + if( pointer === 0 || drop_queued === true ) { + if (kind === 10) { + throw new ReferenceError( "Already dropped Rust function called!" ); + } else if (kind === 12) { + throw new ReferenceError( "Already dropped FnMut function called!" ); + } else { + throw new ReferenceError( "Already called or dropped FnOnce function called!" ); + } + } + + var function_pointer = pointer; + if (kind === 13) { + output.drop = Module.STDWEB_PRIVATE.noop; + pointer = 0; + } + + if (num_ongoing_calls !== 0) { + if (kind === 12 || kind === 13) { + throw new ReferenceError( "FnMut function called multiple times concurrently!" ); + } + } + + var args = Module.STDWEB_PRIVATE.alloc( 16 ); + Module.STDWEB_PRIVATE.serialize_array( args, arguments ); + + try { + num_ongoing_calls += 1; + Module.STDWEB_PRIVATE.dyncall( "vii", adapter_pointer, [function_pointer, args] ); + var result = Module.STDWEB_PRIVATE.tmp; + Module.STDWEB_PRIVATE.tmp = null; + } finally { + num_ongoing_calls -= 1; + } + + if( drop_queued === true && num_ongoing_calls === 0 ) { + output.drop(); + } + + return result; + }; + + output.drop = function() { + if (num_ongoing_calls !== 0) { + drop_queued = true; + return; + } + + output.drop = Module.STDWEB_PRIVATE.noop; + var function_pointer = pointer; + pointer = 0; + + if (function_pointer != 0) { + Module.STDWEB_PRIVATE.dyncall( "vi", deallocator_pointer, [function_pointer] ); + } + }; + + return output; + } else if( kind === 14 ) { + var pointer = Module.HEAPU32[ address / 4 ]; + var length = Module.HEAPU32[ (address + 4) / 4 ]; + var array_kind = Module.HEAPU32[ (address + 8) / 4 ]; + var pointer_end = pointer + length; + + switch( array_kind ) { + case 0: + return Module.HEAPU8.subarray( pointer, pointer_end ); + case 1: + return Module.HEAP8.subarray( pointer, pointer_end ); + case 2: + return Module.HEAPU16.subarray( pointer, pointer_end ); + case 3: + return Module.HEAP16.subarray( pointer, pointer_end ); + case 4: + return Module.HEAPU32.subarray( pointer, pointer_end ); + case 5: + return Module.HEAP32.subarray( pointer, pointer_end ); + case 6: + return Module.HEAPF32.subarray( pointer, pointer_end ); + case 7: + return Module.HEAPF64.subarray( pointer, pointer_end ); + } + } else if( kind === 15 ) { + return Module.STDWEB_PRIVATE.get_raw_value( Module.HEAPU32[ address / 4 ] ); + } +}; + +Module.STDWEB_PRIVATE.serialize_object = function serialize_object( address, value ) { + var keys = Object.keys( value ); + var length = keys.length; + var key_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 8 ); + var value_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 16 ); + Module.HEAPU8[ address + 12 ] = 8; + Module.HEAPU32[ address / 4 ] = value_array_pointer; + Module.HEAPU32[ (address + 4) / 4 ] = length; + Module.HEAPU32[ (address + 8) / 4 ] = key_array_pointer; + for( var i = 0; i < length; ++i ) { + var key = keys[ i ]; + var key_address = key_array_pointer + i * 8; + Module.STDWEB_PRIVATE.to_utf8_string( key_address, key ); + + Module.STDWEB_PRIVATE.from_js( value_array_pointer + i * 16, value[ key ] ); + } +}; + +Module.STDWEB_PRIVATE.serialize_array = function serialize_array( address, value ) { + var length = value.length; + var pointer = Module.STDWEB_PRIVATE.alloc( length * 16 ); + Module.HEAPU8[ address + 12 ] = 7; + Module.HEAPU32[ address / 4 ] = pointer; + Module.HEAPU32[ (address + 4) / 4 ] = length; + for( var i = 0; i < length; ++i ) { + Module.STDWEB_PRIVATE.from_js( pointer + i * 16, value[ i ] ); + } +}; + +// New browsers and recent Node +var cachedEncoder = ( typeof TextEncoder === "function" + ? new TextEncoder( "utf-8" ) + // Old Node (before v11) + : ( typeof util === "object" && util && typeof util.TextEncoder === "function" + ? new util.TextEncoder( "utf-8" ) + // Old browsers + : null ) ); + +if ( cachedEncoder != null ) { + Module.STDWEB_PRIVATE.to_utf8_string = function to_utf8_string( address, value ) { + var buffer = cachedEncoder.encode( value ); + var length = buffer.length; + var pointer = 0; + + if ( length > 0 ) { + pointer = Module.STDWEB_PRIVATE.alloc( length ); + Module.HEAPU8.set( buffer, pointer ); + } + + Module.HEAPU32[ address / 4 ] = pointer; + Module.HEAPU32[ (address + 4) / 4 ] = length; + }; + +} else { + Module.STDWEB_PRIVATE.to_utf8_string = function to_utf8_string( address, value ) { + var length = Module.STDWEB_PRIVATE.utf8_len( value ); + var pointer = 0; + + if ( length > 0 ) { + pointer = Module.STDWEB_PRIVATE.alloc( length ); + Module.STDWEB_PRIVATE.to_utf8( value, pointer ); + } + + Module.HEAPU32[ address / 4 ] = pointer; + Module.HEAPU32[ (address + 4) / 4 ] = length; + }; +} + +Module.STDWEB_PRIVATE.from_js = function from_js( address, value ) { + var kind = Object.prototype.toString.call( value ); + if( kind === "[object String]" ) { + Module.HEAPU8[ address + 12 ] = 4; + Module.STDWEB_PRIVATE.to_utf8_string( address, value ); + } else if( kind === "[object Number]" ) { + if( value === (value|0) ) { + Module.HEAPU8[ address + 12 ] = 2; + Module.HEAP32[ address / 4 ] = value; + } else { + Module.HEAPU8[ address + 12 ] = 3; + Module.HEAPF64[ address / 8 ] = value; + } + } else if( value === null ) { + Module.HEAPU8[ address + 12 ] = 1; + } else if( value === undefined ) { + Module.HEAPU8[ address + 12 ] = 0; + } else if( value === false ) { + Module.HEAPU8[ address + 12 ] = 5; + } else if( value === true ) { + Module.HEAPU8[ address + 12 ] = 6; + } else if( kind === "[object Symbol]" ) { + var id = Module.STDWEB_PRIVATE.register_raw_value( value ); + Module.HEAPU8[ address + 12 ] = 15; + Module.HEAP32[ address / 4 ] = id; + } else { + var refid = Module.STDWEB_PRIVATE.acquire_rust_reference( value ); + Module.HEAPU8[ address + 12 ] = 9; + Module.HEAP32[ address / 4 ] = refid; + } +}; + +// New browsers and recent Node +var cachedDecoder = ( typeof TextDecoder === "function" + ? new TextDecoder( "utf-8" ) + // Old Node (before v11) + : ( typeof util === "object" && util && typeof util.TextDecoder === "function" + ? new util.TextDecoder( "utf-8" ) + // Old browsers + : null ) ); + +if ( cachedDecoder != null ) { + Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) { + return cachedDecoder.decode( Module.HEAPU8.subarray( index, index + length ) ); + }; + +} else { + // This is ported from Rust's stdlib; it's faster than + // the string conversion from Emscripten. + Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) { + var HEAPU8 = Module.HEAPU8; + index = index|0; + length = length|0; + var end = (index|0) + (length|0); + var output = ""; + while( index < end ) { + var x = HEAPU8[ index++ ]; + if( x < 128 ) { + output += String.fromCharCode( x ); + continue; + } + var init = (x & (0x7F >> 2)); + var y = 0; + if( index < end ) { + y = HEAPU8[ index++ ]; + } + var ch = (init << 6) | (y & 63); + if( x >= 0xE0 ) { + var z = 0; + if( index < end ) { + z = HEAPU8[ index++ ]; + } + var y_z = ((y & 63) << 6) | (z & 63); + ch = init << 12 | y_z; + if( x >= 0xF0 ) { + var w = 0; + if( index < end ) { + w = HEAPU8[ index++ ]; + } + ch = (init & 7) << 18 | ((y_z << 6) | (w & 63)); + + output += String.fromCharCode( 0xD7C0 + (ch >> 10) ); + ch = 0xDC00 + (ch & 0x3FF); + } + } + output += String.fromCharCode( ch ); + continue; + } + return output; + }; +} + +Module.STDWEB_PRIVATE.id_to_ref_map = {}; +Module.STDWEB_PRIVATE.id_to_refcount_map = {}; +Module.STDWEB_PRIVATE.ref_to_id_map = new WeakMap(); +// Not all types can be stored in a WeakMap +Module.STDWEB_PRIVATE.ref_to_id_map_fallback = new Map(); +Module.STDWEB_PRIVATE.last_refid = 1; + +Module.STDWEB_PRIVATE.id_to_raw_value_map = {}; +Module.STDWEB_PRIVATE.last_raw_value_id = 1; + +Module.STDWEB_PRIVATE.acquire_rust_reference = function( reference ) { + if( reference === undefined || reference === null ) { + return 0; + } + + var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map; + var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map; + var ref_to_id_map = Module.STDWEB_PRIVATE.ref_to_id_map; + var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback; + + var refid = ref_to_id_map.get( reference ); + if( refid === undefined ) { + refid = ref_to_id_map_fallback.get( reference ); + } + if( refid === undefined ) { + refid = Module.STDWEB_PRIVATE.last_refid++; + try { + ref_to_id_map.set( reference, refid ); + } catch (e) { + ref_to_id_map_fallback.set( reference, refid ); + } + } + + if( refid in id_to_ref_map ) { + id_to_refcount_map[ refid ]++; + } else { + id_to_ref_map[ refid ] = reference; + id_to_refcount_map[ refid ] = 1; + } + + return refid; +}; + +Module.STDWEB_PRIVATE.acquire_js_reference = function( refid ) { + return Module.STDWEB_PRIVATE.id_to_ref_map[ refid ]; +}; + +Module.STDWEB_PRIVATE.increment_refcount = function( refid ) { + Module.STDWEB_PRIVATE.id_to_refcount_map[ refid ]++; +}; + +Module.STDWEB_PRIVATE.decrement_refcount = function( refid ) { + var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map; + if( 0 == --id_to_refcount_map[ refid ] ) { + var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map; + var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback; + var reference = id_to_ref_map[ refid ]; + delete id_to_ref_map[ refid ]; + delete id_to_refcount_map[ refid ]; + ref_to_id_map_fallback.delete(reference); + } +}; + +Module.STDWEB_PRIVATE.register_raw_value = function( value ) { + var id = Module.STDWEB_PRIVATE.last_raw_value_id++; + Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ] = value; + return id; +}; + +Module.STDWEB_PRIVATE.unregister_raw_value = function( id ) { + delete Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ]; +}; + +Module.STDWEB_PRIVATE.get_raw_value = function( id ) { + return Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ]; +}; + +Module.STDWEB_PRIVATE.alloc = function alloc( size ) { + return Module.web_malloc( size ); +}; + +Module.STDWEB_PRIVATE.dyncall = function( signature, ptr, args ) { + return Module.web_table.get( ptr ).apply( null, args ); +}; + +// This is based on code from Emscripten's preamble.js. +Module.STDWEB_PRIVATE.utf8_len = function utf8_len( str ) { + var len = 0; + for( var i = 0; i < str.length; ++i ) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var u = str.charCodeAt( i ); // possibly a lead surrogate + if( u >= 0xD800 && u <= 0xDFFF ) { + u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF); + } + + if( u <= 0x7F ) { + ++len; + } else if( u <= 0x7FF ) { + len += 2; + } else if( u <= 0xFFFF ) { + len += 3; + } else if( u <= 0x1FFFFF ) { + len += 4; + } else if( u <= 0x3FFFFFF ) { + len += 5; + } else { + len += 6; + } + } + return len; +}; + +Module.STDWEB_PRIVATE.prepare_any_arg = function( value ) { + var arg = Module.STDWEB_PRIVATE.alloc( 16 ); + Module.STDWEB_PRIVATE.from_js( arg, value ); + return arg; +}; + +Module.STDWEB_PRIVATE.acquire_tmp = function( dummy ) { + var value = Module.STDWEB_PRIVATE.tmp; + Module.STDWEB_PRIVATE.tmp = null; + return value; +}; + + + + var HEAP8 = null; + var HEAP16 = null; + var HEAP32 = null; + var HEAPU8 = null; + var HEAPU16 = null; + var HEAPU32 = null; + var HEAPF32 = null; + var HEAPF64 = null; + + Object.defineProperty( Module, 'exports', { value: {} } ); + + function __web_on_grow() { + var buffer = Module.instance.exports.memory.buffer; + HEAP8 = new Int8Array( buffer ); + HEAP16 = new Int16Array( buffer ); + HEAP32 = new Int32Array( buffer ); + HEAPU8 = new Uint8Array( buffer ); + HEAPU16 = new Uint16Array( buffer ); + HEAPU32 = new Uint32Array( buffer ); + HEAPF32 = new Float32Array( buffer ); + HEAPF64 = new Float64Array( buffer ); + Module.HEAP8 = HEAP8; + Module.HEAP16 = HEAP16; + Module.HEAP32 = HEAP32; + Module.HEAPU8 = HEAPU8; + Module.HEAPU16 = HEAPU16; + Module.HEAPU32 = HEAPU32; + Module.HEAPF32 = HEAPF32; + Module.HEAPF64 = HEAPF64; + } + + return { + imports: { + env: { + "__cargo_web_snippet_09675c7ed2827e045dc760aeac3d286437cfbe5e": function($0, $1, $2, $3) { + $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){try{return{value:function(){return($1).setAttribute(($2),($3));}(),success:true};}catch(error){return{error:error,success:false};}})()); + }, + "__cargo_web_snippet_0e54fd9c163fcf648ce0a395fde4500fd167a40b": function($0) { + var r = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (r instanceof DOMException) && (r.name === "InvalidCharacterError"); + }, + "__cargo_web_snippet_1edaec034bdcb0a749c6d5de76c29f6371afb5a0": function($0) { + var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof Event && o.type === "input"); + }, + "__cargo_web_snippet_2908dbb08792df5e699e324eec3e29fd6a57c2c9": function($0) { + var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof HTMLInputElement); + }, + "__cargo_web_snippet_3c5e83d16a83fc7147ec91e2506438012952f55a": function($0) { + var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof Element); + }, + "__cargo_web_snippet_614a3dd2adb7e9eac4a0ec6e59d37f87e0521c3b": function($0, $1) { + $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).error;})()); + }, + "__cargo_web_snippet_6fcce0aae651e2d748e085ff1f800f87625ff8c8": function($0) { + Module.STDWEB_PRIVATE.from_js($0, (function(){return document;})()); + }, + "__cargo_web_snippet_72fc447820458c720c68d0d8e078ede631edd723": function($0, $1, $2) { + console.error( 'Panic location:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) + ':' + $2 ); + }, + "__cargo_web_snippet_80d6d56760c65e49b7be8b6b01c1ea861b046bf0": function($0) { + Module.STDWEB_PRIVATE.decrement_refcount( $0 ); + }, + "__cargo_web_snippet_91749aeb589cd0f9b17cbc01b2872ba709817982": function($0, $1, $2) { + $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){try{return{value:function(){return($1).createElement(($2));}(),success:true};}catch(error){return{error:error,success:false};}})()); + }, + "__cargo_web_snippet_97495987af1720d8a9a923fa4683a7b683e3acd6": function($0, $1) { + console.error( 'Panic error message:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) ); + }, + "__cargo_web_snippet_99c4eefdc8d4cc724135163b8c8665a1f3de99e4": function($0, $1, $2, $3) { + $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){var listener=($1);($2).addEventListener(($3),listener);return listener;})()); + }, + "__cargo_web_snippet_9f22d4ca7bc938409787341b7db181f8dd41e6df": function($0) { + Module.STDWEB_PRIVATE.increment_refcount( $0 ); + }, + "__cargo_web_snippet_ab05f53189dacccf2d365ad26daa407d4f7abea9": function($0, $1) { + $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).value;})()); + }, + "__cargo_web_snippet_afafe9a462a05084fec65cacc7d6598e145ff3e3": function($0, $1, $2) { + $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).createTextNode(($2));})()); + }, + "__cargo_web_snippet_b06dde4acf09433b5190a4b001259fe5d4abcbc2": function($0, $1) { + $1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).success;})()); + }, + "__cargo_web_snippet_d5e30f74cb752784e06bd97a37b1f89b6c3433a7": function($0, $1, $2) { + $1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).getElementById(($2));})()); + }, + "__cargo_web_snippet_dc2fd915bd92f9e9c6a3bd15174f1414eee3dbaf": function() { + console.error( 'Encountered a panic!' ); + }, + "__cargo_web_snippet_e741b9d9071097746386b2c2ec044a2bc73e688c": function($0, $1) { + $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0).appendChild(($1)); + }, + "__cargo_web_snippet_e9638d6405ab65f78daf4a5af9c9de14ecf1e2ec": function($0) { + $0 = Module.STDWEB_PRIVATE.to_js($0);Module.STDWEB_PRIVATE.unregister_raw_value(($0)); + }, + "__cargo_web_snippet_f765b15a1a1b5cd266e922e6fca98dd570f17edc": function($0, $1) { + $0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0).textContent=($1); + }, + "__cargo_web_snippet_ff5103e6cc179d13b4c7a785bdce2708fd559fc0": function($0) { + Module.STDWEB_PRIVATE.tmp = Module.STDWEB_PRIVATE.to_js( $0 ); + }, + "__web_on_grow": __web_on_grow + } + }, + initialize: function( instance ) { + Object.defineProperty( Module, 'instance', { value: instance } ); + Object.defineProperty( Module, 'web_malloc', { value: Module.instance.exports.__web_malloc } ); + Object.defineProperty( Module, 'web_free', { value: Module.instance.exports.__web_free } ); + Object.defineProperty( Module, 'web_table', { value: Module.instance.exports.__indirect_function_table } ); + + + __web_on_grow(); + Module.instance.exports.main(); + + return Module.exports; + } + }; +} + )); +})); diff --git a/content/enterprise/prototype/helloworld.wasm b/content/enterprise/prototype/helloworld.wasm new file mode 100644 index 0000000..3d6ced2 Binary files /dev/null and b/content/enterprise/prototype/helloworld.wasm differ diff --git a/content/enterprise/prototype/index.md b/content/enterprise/prototype/index.md new file mode 100644 index 0000000..2e1094c --- /dev/null +++ b/content/enterprise/prototype/index.md @@ -0,0 +1,52 @@ ++++ +title = "enterprise: a new ui framework" +date = 2020-02-11 +draft = true +template = "post.html" + +[taxonomies] +tags = [] + +[extra] +toc = true ++++ + +This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the ideation and the implementation process. + +## Goals of the project + +* **Complete separation of business logic from UI.** Theoretically, one could completely retarget the application to a completely different platform (mobile, web, native, something new that will pop up in 5 years), without changing any of the core logic. As enterprise grows to include a backend, this should be true of the backend as well. It does this by introducing [DSL][2]s that are completely architecture-independent. +* **Frontend relationships should be as static as possible.** + +## Prototype + +The prototype for experimenting is a simple "Hello, world" application. If you've looked at any web framework before, this is probably one of the simplest examples of bindings: type something into a box and watch as the text magically populates with whatever you wrote in the box. If you're using a WASM-compatible browser with JavaScript enabled, you should be able to try out the demo in real-time: + +
+ + +OK, you say, but I could implement this in 3 lines of JavaScript. + +```js +inputEl.addEventListener("change", () => { + spanEl.innerText = inputEl.value; +}); +``` + +Surely, this works, but it doesn't scale. If you try to write a page full of these kind of bindings directly using JavaScript, you're either going to start running into bugs or building up a pile of unmaintainable spaghetti code. + +So how does enterprise represent this? Well, the enterprise DSL has no concrete syntax yet, but if it did, it would look something like this: + +``` +model { + name: String, +} + +view { +