change index.js to es6 modules, rollup to create cjs version, package.json to use main/module to differentiate
ADD
index-commonjs.js
+'use strict';
+
+// Adapted from SES/Caja - Copyright (C) 2011 Google Inc.
+// Copyright (C) 2018 Agoric
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// based upon:
+// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js
+// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js
+// then copied from proposal-frozen-realms deep-freeze.js
+// then copied from SES/src/bundle/deepFreeze.js
+
+function makeHardener(initialFringe) {
+ const { freeze, getOwnPropertyDescriptors, getPrototypeOf } = Object;
+ const { ownKeys } = Reflect;
+ // Objects that we won't freeze, either because we've frozen them already,
+ // or they were one of the initial roots (terminals). These objects form
+ // the "fringe" of the hardened object graph.
+ const fringeSet = new WeakSet(initialFringe);
+
+ function harden(root) {
+ const toFreeze = new Set();
+ const prototypes = new Map();
+ const paths = new WeakMap();
+
+ // If val is something we should be freezing but aren't yet,
+ // add it to toFreeze.
+ function enqueue(val, path) {
+ if (Object(val) !== val) {
+ // ignore primitives
+ return;
+ }
+ const type = typeof val;
+ if (type !== 'object' && type !== 'function') {
+ // future proof: break until someone figures out what it should do
+ throw new TypeError(`Unexpected typeof: ${type}`);
+ }
+ if (fringeSet.has(val) || toFreeze.has(val)) {
+ // Ignore if this is an exit, or we've already visited it
+ return;
+ }
+ // console.log(`adding ${val} to toFreeze`, val);
+ toFreeze.add(val);
+ paths.set(val, path);
+ }
+
+ function freezeAndTraverse(obj) {
+ // Immediately freeze the object to ensure reactive
+ // objects such as proxies won't add properties
+ // during traversal, before they get frozen.
+
+ // Object are verified before being enqueued,
+ // therefore this is a valid candidate.
+ // Throws if this fails (strict mode).
+ freeze(obj);
+
+ // we rely upon certain commitments of Object.freeze and proxies here
+
+ // get stable/immutable outbound links before a Proxy has a chance to do
+ // something sneaky.
+ const proto = getPrototypeOf(obj);
+ const descs = getOwnPropertyDescriptors(obj);
+ const path = paths.get(obj) || 'unknown';
+
+ // console.log(`adding ${proto} to prototypes under ${path}`);
+ if (!prototypes.has(proto)) {
+ prototypes.set(proto, path);
+ paths.set(proto, `${path}.__proto__`);
+ }
+
+ ownKeys(descs).forEach(name => {
+ const pathname = `${path}.${String(name)}`;
+ // todo uncurried form
+ // todo: getOwnPropertyDescriptors is guaranteed to return well-formed
+ // descriptors, but they still inherit from Object.prototype. If
+ // someone has poisoned Object.prototype to add 'value' or 'get'
+ // properties, then a simple 'if ("value" in desc)' or 'desc.value'
+ // test could be confused. We use hasOwnProperty to be sure about
+ // whether 'value' is present or not, which tells us for sure that this
+ // is a data property.
+ const desc = descs[name];
+ if ('value' in desc) {
+ // todo uncurried form
+ enqueue(desc.value, `${pathname}`);
+ } else {
+ enqueue(desc.get, `${pathname}(get)`);
+ enqueue(desc.set, `${pathname}(set)`);
+ }
+ });
+ }
+
+ function dequeue() {
+ // New values added before forEach() has finished will be visited.
+ toFreeze.forEach(freezeAndTraverse); // todo curried forEach
+ }
+
+ function checkPrototypes() {
+ prototypes.forEach((path, p) => {
+ if (!(toFreeze.has(p) || fringeSet.has(p))) {
+ // all reachable properties have already been frozen by this point
+ throw new TypeError(
+ `prototype ${p} of ${path} is not already in the fringeSet`,
+ );
+ }
+ });
+ }
+
+ function commit() {
+ // todo curried forEach
+ // we capture the real WeakSet.prototype.add above, in case someone
+ // changes it. The two-argument form of forEach passes the second
+ // argument as the 'this' binding, so we add to the correct set.
+ toFreeze.forEach(fringeSet.add, fringeSet);
+ }
+
+ enqueue(root);
+ dequeue();
+ // console.log("fringeSet", fringeSet);
+ // console.log("prototype set:", prototypes);
+ // console.log("toFreeze set:", toFreeze);
+ checkPrototypes();
+ commit();
+
+ return root;
+ }
+
+ return harden;
+}
+
+module.exports = makeHardener;

MODIFY
index.js
index.js
@@ -136,4 +136,4 @@ function makeHardener(initialFringe) {
return harden;
}
-module.exports = makeHardener;
+export default makeHardener;

MODIFY
package-lock.json
package-lock.json
"any-observable": "^0.3.0"
}
},
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "11.9.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz",
+ "integrity": "sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==",
+ "dev": true
+ },
"acorn": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz",
"glob": "^7.1.3"
}
},
+ "rollup": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.2.2.tgz",
+ "integrity": "sha512-fsn5KJcfSuejjrv8GV7kZNciElqxyzZdUq8rA3e528JsR3ccxrWwoptyUY8GGLlgMFAQMB3dZW8nWF2I1/xrZA==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "@types/node": "*",
+ "acorn": "^6.1.0"
+ }
+ },
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",

MODIFY
package.json
package.json
"name": "@agoric/make-hardener",
"version": "0.0.3",
"description": "Create a 'hardener' which freezes the API surface of a set of objects",
- "main": "index.js",
+ "main": "index-commonjs.js",
+ "module": "index.js",
"scripts": {
"test": "node test/test.js",
"pretty-fix": "prettier --write '**/*.{js,jsx}'",
"lint": "eslint '**/*.{js,jsx}'",
"pretty-check": "prettier --check '**/*.{js,jsx}'",
"lint-fix": "eslint --fix '**/*.{js,jsx}'",
- "lint-check": "eslint '**/*.{js,jsx}'"
+ "lint-check": "eslint '**/*.{js,jsx}'",
+ "create-cjs": "rollup index.js --file index-commonjs.js --format cjs"
},
"repository": {
"type": "git",
"husky": "^1.3.1",
"lint-staged": "^8.1.3",
"prettier": "1.16.4",
+ "rollup": "^1.2.2",
"tape": "^4.9.2"
},
"lint-staged": {

MODIFY
test/test.js
test/test.js
const test = require('tape');
-const makeHardener = require('../index.js');
+const makeHardener = require('../index-commonjs.js');
test('makeHardener', t => {
const h = makeHardener([Object.prototype]);

change index.js to es6 modules, rollup to create cjs version, package.json to use main/module to differentiate