diff --git a/gatsby-node.js b/gatsby-node.js index 857a6cfed..ca1b68bc0 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -4,4 +4,13 @@ * See: https://www.gatsbyjs.org/docs/node-apis/ */ -// You can delete this file if you're not using it \ No newline at end of file +// You can delete this file if you're not using it + +exports.onCreateWebpackConfig = ({ stage, actions }) => { + actions.setWebpackConfig({ + + //allow us to not use ENVIRONMENT=web when compiling anypia-js, test it + // https://stackoverflow.com/questions/59487224/webpack-throws-error-with-emscripten-cant-resolve-fs + "node": { "fs": "empty" } + }) + } diff --git a/package-lock.json b/package-lock.json index b0b312b34..a8ee0eb40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5880,9 +5880,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001118", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001118.tgz", - "integrity": "sha512-RNKPLojZo74a0cP7jFMidQI7nvLER40HgNfgKQEJ2PFm225L0ectUungNQoK3Xk3StQcFbpBPNEvoWD59436Hg==" + "version": "1.0.30001198", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001198.tgz", + "integrity": "sha512-r5GGgESqOPZzwvdLVER374FpQu2WluCF1Z2DSiFJ89KSmGjT0LVKjgv4NcAqHmGWF9ihNpqRI9KXO9Ex4sKsgA==" }, "capture-exit": { "version": "2.0.0", diff --git a/package.json b/package.json index 6f0d0fafd..2276cf0da 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@types/react-helmet": "^6.1.0", "@types/use-persisted-state": "^0.3.0", "babel-preset-gatsby": "^0.5.6", + "caniuse-lite": "^1.0.30001198", "core-js": "3.6.5", "d3": "^6.2.0", "d3-format": "^1.4.3", diff --git a/src/library/anypiajs.mjs b/src/library/anypiajs.mjs index 472f0635e..d241a5c4b 100644 --- a/src/library/anypiajs.mjs +++ b/src/library/anypiajs.mjs @@ -10,7 +10,7 @@ import 'core-js/modules/es6.typed-array.uint8-clamped-array' // See: https://stackoverflow.com/questions/63592691/core-js-cannot-resolve-core-js-modules-es6-typed-uint32-array/63592692#63592692 var Module = (function() { - var _scriptDir = '/anypiajs.wasm'; + var _scriptDir = '/anypiajs-20201.wasm'; return ( function(Module) { @@ -193,10 +193,16 @@ var quit_ = function(status, toThrow) { // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). -var ENVIRONMENT_IS_WEB = true; +var ENVIRONMENT_IS_WEB = false; var ENVIRONMENT_IS_WORKER = false; var ENVIRONMENT_IS_NODE = false; var ENVIRONMENT_IS_SHELL = false; +ENVIRONMENT_IS_WEB = typeof window === 'object'; +ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; +ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; if (Module['ENVIRONMENT']) { throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)'); @@ -219,6 +225,104 @@ var read_, readBinary, setWindowTitle; +var nodeFS; +var nodePath; + +if (ENVIRONMENT_IS_NODE) { + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = require('path').dirname(scriptDirectory) + '/'; + } else { + scriptDirectory = __dirname + '/'; + } + + + + + read_ = function shell_read(filename, binary) { + if (!nodeFS) nodeFS = require('fs'); + if (!nodePath) nodePath = require('path'); + filename = nodePath['normalize'](filename); + return nodeFS['readFileSync'](filename, binary ? null : 'utf8'); + }; + + readBinary = function readBinary(filename) { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; + }; + + + + + if (process['argv'].length > 1) { + thisProgram = process['argv'][1].replace(/\\/g, '/'); + } + + arguments_ = process['argv'].slice(2); + + // MODULARIZE will export the module in the proper place outside, we don't need to export here + + process['on']('uncaughtException', function(ex) { + // suppress ExitStatus exceptions from showing an error + if (!(ex instanceof ExitStatus)) { + throw ex; + } + }); + + process['on']('unhandledRejection', abort); + + quit_ = function(status) { + process['exit'](status); + }; + + Module['inspect'] = function () { return '[Emscripten Module object]'; }; + + + +} else +if (ENVIRONMENT_IS_SHELL) { + + + if (typeof read != 'undefined') { + read_ = function shell_read(f) { + return read(f); + }; + } + + readBinary = function readBinary(f) { + var data; + if (typeof readbuffer === 'function') { + return new Uint8Array(readbuffer(f)); + } + data = read(f, 'binary'); + assert(typeof data === 'object'); + return data; + }; + + if (typeof scriptArgs != 'undefined') { + arguments_ = scriptArgs; + } else if (typeof arguments != 'undefined') { + arguments_ = arguments; + } + + if (typeof quit === 'function') { + quit_ = function(status) { + quit(status); + }; + } + + if (typeof print !== 'undefined') { + // Prefer to use print/printErr where they exist, as they usually work better. + if (typeof console === 'undefined') console = /** @type{!Console} */({}); + console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); + console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr !== 'undefined' ? printErr : print); + } + + +} else // Note that this includes Node.js workers when relevant (pthreads is enabled). // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and @@ -244,7 +348,6 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { scriptDirectory = ''; } - if (!(typeof window === 'object' || typeof importScripts === 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); // Differentiate the Web Worker from the Node Worker case, as reading must // be done differently. @@ -1359,11 +1462,11 @@ function updateGlobalBufferAndViews(buf) { } var STATIC_BASE = 1024, - STACK_BASE = 74208, + STACK_BASE = 74496, STACKTOP = STACK_BASE, - STACK_MAX = 5317088, - DYNAMIC_BASE = 5317088, - DYNAMICTOP_PTR = 74000; + STACK_MAX = 5317376, + DYNAMIC_BASE = 5317376, + DYNAMICTOP_PTR = 74288; assert(STACK_BASE % 16 === 0, 'stack must start aligned'); assert(DYNAMIC_BASE % 16 === 0, 'heap must start aligned'); @@ -1787,7 +1890,7 @@ function createExportWrapper(name, fixedasm) { }; } -var wasmBinaryFile = 'anypiajs.wasm'; +var wasmBinaryFile = 'anypiajs-20201.wasm'; if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile); } @@ -1813,6 +1916,8 @@ function getBinaryPromise() { // If we don't have the binary yet, and have the Fetch api, use that; // in some environments, like Electron's render process, Fetch api may be present, but have a different context than expected, let's only use it on the Web if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) && typeof fetch === 'function' + // Let's not use fetch to get objects over file:// as it's most likely Cordova which doesn't support fetch for file:// + && !isFileURI(wasmBinaryFile) ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { if (!response['ok']) { @@ -1890,6 +1995,8 @@ function createWasm() { if (!wasmBinary && typeof WebAssembly.instantiateStreaming === 'function' && !isDataURI(wasmBinaryFile) && + // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(wasmBinaryFile) && typeof fetch === 'function') { fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { var result = WebAssembly.instantiateStreaming(response, info); @@ -1936,7 +2043,7 @@ var ASM_CONSTS = []; -// STATICTOP = STATIC_BASE + 73184; +// STATICTOP = STATIC_BASE + 73472; /* global initializers */ __ATINIT__.push({ func: function() { globalCtors() } }); @@ -1947,7 +2054,7 @@ var ASM_CONSTS = []; /* no memory initializer */ -var tempDoublePtr = 74192; +var tempDoublePtr = 74480; function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much HEAP8[tempDoublePtr] = HEAP8[ptr]; @@ -2290,6 +2397,27 @@ function copyTempDouble(ptr) { }},default_tty_ops:{get_char:function(tty) { if (!tty.input.length) { var result = null; + if (ENVIRONMENT_IS_NODE) { + // we will read data by chunks of BUFSIZE + var BUFSIZE = 256; + var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE); + var bytesRead = 0; + + try { + bytesRead = nodeFS.readSync(process.stdin.fd, buf, 0, BUFSIZE, null); + } catch(e) { + // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes, + // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0. + if (e.toString().indexOf('EOF') != -1) bytesRead = 0; + else throw e; + } + + if (bytesRead > 0) { + result = buf.slice(0, bytesRead).toString('utf-8'); + } else { + result = null; + } + } else if (typeof window != 'undefined' && typeof window.prompt == 'function') { // Browser. @@ -3735,6 +3863,16 @@ function copyTempDouble(ptr) { var randomBuffer = new Uint8Array(1); random_device = function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; } else + if (ENVIRONMENT_IS_NODE) { + // for nodejs with or without crypto support included + try { + var crypto_module = require('crypto'); + // nodejs has crypto support + random_device = function() { return crypto_module['randomBytes'](1)[0]; }; + } catch (e) { + // nodejs doesn't have crypto support + } + } else {} if (!random_device) { // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 @@ -6206,7 +6344,7 @@ function copyTempDouble(ptr) { } - var ___tm_timezone=(stringToUTF8("GMT", 74096, 4), 74096); + var ___tm_timezone=(stringToUTF8("GMT", 74384, 4), 74384); function _tzset() { // TODO: Use (malleable) environment variables instead of system settings. @@ -6947,8 +7085,8 @@ var dynCall_viiiiii = Module["dynCall_viiiiii"] = createExportWrapper("dynCall_v /** @type {function(...*):?} */ var dynCall_viijii = Module["dynCall_viijii"] = createExportWrapper("dynCall_viijii"); -Module['__ZZNKSt3__27num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKvE5__fmt'] = 62561; -Module['__ZZNKSt3__27num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwmE5__fmt'] = 62572;; +Module['__ZZNKSt3__27num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwPKvE5__fmt'] = 62847; +Module['__ZZNKSt3__27num_putIwNS_19ostreambuf_iteratorIwNS_11char_traitsIwEEEEE6do_putES4_RNS_8ios_baseEwmE5__fmt'] = 62858;; diff --git a/src/library/pia/index.ts b/src/library/pia/index.ts index 6d0b95d9b..4e5e5b0dd 100644 --- a/src/library/pia/index.ts +++ b/src/library/pia/index.ts @@ -185,7 +185,8 @@ export async function finalCalculation( aime: calculation.AIME && calculation.AIME.AME, fullRetireDate: new Date("2040-1-1").toLocaleDateString("en-US"), calculatorType: 'anypia', - bendPoints: usePIAAfterWindwfall ? calculation.PIAAfterWindwfall : calculation.PIAEligibility + bendPoints: usePIAAfterWindwfall ? calculation.PIAAfterWindwfall : calculation.PIAEligibility, + calculatorApp: resultObj.App }; console.log("usePIAAfterWindwfall", usePIAAfterWindwfall); console.warn("DUMMY fullRetireDate until it can be added:"); diff --git a/src/library/user-state-context.tsx b/src/library/user-state-context.tsx index 14d15f007..097aaa3fe 100644 --- a/src/library/user-state-context.tsx +++ b/src/library/user-state-context.tsx @@ -42,6 +42,12 @@ interface BendPoints { // HIGH = "HIGH", // } +export interface CalculatorApp { + Description: string; + Name: string; + Version: string; +} + // Calculated results for the user export interface UserProfile { "Standard PIA": string; @@ -54,6 +60,7 @@ export interface UserProfile { fullRetireDate: string; calculatorType: string; bendPoints?: BendPoints[] + calculatorApp?: CalculatorApp; } export interface UserState { diff --git a/src/pages/screen-2.tsx b/src/pages/screen-2.tsx index 17b5ba251..6b2434d9c 100644 --- a/src/pages/screen-2.tsx +++ b/src/pages/screen-2.tsx @@ -54,6 +54,10 @@ const Text = styled.div` font-size: ${fontSizes[1]}; `; +const GlossaryContainer = styled.div` + margin-left: 20px; +`; + interface Screen2Props { userState: UserState; userStateActions: UserStateActions; @@ -383,20 +387,15 @@ export class Screen2 extends React.Component { ) : null} {preferPiaUserCalc && ( - -
- - The current version of the Social Security Detailed - Calculator is 2020.1, which was released on December 31, - 2019. It updates the 2019.2 version with the new economic - information from the automatic adjustments announced on - October 10, 2019. - -
+ + + {userProfile.calculatorApp?.Description} + + )} diff --git a/src/test/pia-index.test.ts b/src/test/pia-index.test.ts index a53b5786d..43a2df5bc 100644 --- a/src/test/pia-index.test.ts +++ b/src/test/pia-index.test.ts @@ -1,46 +1,71 @@ // TODO ENABLE NODEJS mode for Emscripten Module and uncomment this file // will not work without this. Also need to find way to reference wasm file for tests since /anypiajs.wasm won't workA -// import { finalCalculation } from "../library/pia/index"; -// import { delayedRetirementValues, fullRetirementValues, sample20RetirementValues } from "../library/testFiles"; -// import { -// finalCalculation as originalFinalCalculation, -// getRawEarnings, -// } from "../library/observable-functions"; +import { finalCalculation } from "../library/pia/index"; +import { delayedRetirementValues, fullRetirementValues, sample20RetirementValues } from "../library/testFiles"; +import { + finalCalculation as originalFinalCalculation, + getRawEarnings, +} from "../library/observable-functions"; +import dayjs from "dayjs"; + + describe("Run AnyPIAJS", () => { - it("once it is recompiled with NodeJS support", async () => { + it("Now it has NodeJS support", async () => { expect.assertions(1); expect(null).toBe(null); }); }); +// TODO: +// Do an approach like this: https://github.com/emscripten-core/emscripten/issues/8400#issuecomment-498218291 +describe("John Q. Public (Full Retirement)", async () => { + + beforeAll(async () => { + var loader = ???(); + loader.ready = () => + // https://github.com/emscripten-core/emscripten/issues/5820 + new Promise((resolve, reject) => { + delete loader.then; + loader.onAbort = reject; + loader.addOnPostRun(() => { + resolve(loader); + }); + }); + ??? = await loader.ready(); +}); -// describe("John Q. Public (Full Retirement)", async () => { -// const earnings = -// fullRetirementValues["osss:OnlineSocialSecurityStatementData"][ -// "osss:EarningsRecord" -// ]["osss:Earnings"]; -// const userDOB = new Date("1947-10-10"); -// const userDOR = new Date("2013-10-10"); // 66 is their full retirement age -// const rawEarnings = getRawEarnings(earnings); -// const userPension = 0; - -// const piaUserCal = await finalCalculation( -// userDOB, -// userDOR, -// userPension, -// rawEarnings -// ); -// // var userCalc = await finalCalculation(userDOB, userDOR, year62, userYSE, userPension, userAIME) - -// const userYSE = Number(piaUserCal["yearsSubstantialEarnings"]); - -// it("Correctly tallies years of substantial earnings from a full earnings record.", async () => { -// expect.assertions(1); + const earnings = + fullRetirementValues["osss:OnlineSocialSecurityStatementData"][ + "osss:EarningsRecord" + ]["osss:Earnings"]; + + const userDOB = "1947-10-10"; + const userDOR = dayjs("2013-10-10").toDate(); // 66yo is their full retirement age. + //const year62 = "2014"; + const rawEarnings = getRawEarnings(earnings); + const userPension = 0; + + const piaUserCal = await finalCalculation( + userDOB, + userDOR, + userPension, + rawEarnings, + null, + null, + null,null + ); + // var userCalc = await finalCalculation(userDOB, userDOR, year62, userYSE, userPension, userAIME) + + const userYSE = Number(piaUserCal["yearsSubstantialEarnings"]); + + it("Correctly tallies years of substantial earnings from a full earnings record.", async () => { + expect.assertions(1); -// expect(userYSE).toBe(30); -// }); + expect(userYSE).toBe(30); + }); +}); // it("Correctly calculates AIME from a full earnings record.", async () => { // expect.assertions(1); diff --git a/static/anypiajs-20201.wasm b/static/anypiajs-20201.wasm new file mode 100644 index 000000000..c001b78dd Binary files /dev/null and b/static/anypiajs-20201.wasm differ