flamecoals-boardgame.io
Version:
library for turn-based games
1,642 lines (1,407 loc) • 107 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
var Flatted = (function (Primitive, primitive) {
/*!
* ISC License
*
* Copyright (c) 2018, Andrea Giammarchi, @WebReflection
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
var Flatted = {
parse: function parse(text) {
var input = JSON.parse(text, Primitives).map(primitives);
var value = input[0];
return typeof value === 'object' && value ?
revive(input, new Set, value) : value;
},
stringify: function stringify(value) {
for (var
firstRun,
known = new Map,
input = [],
output = [],
i = +set(known, input, value),
replace = function (key, value) {
if (firstRun) return (firstRun = !firstRun), value;
switch (typeof value) {
case 'object':
if (value === null) return value;
case primitive:
return known.get(value) || set(known, input, value);
}
return value;
};
i < input.length; i++
) {
firstRun = true;
output[i] = JSON.stringify(input[i], replace);
}
return '[' + output.join(',') + ']';
}
};
return Flatted;
function revive(input, parsed, output) {
return Object.keys(output).reduce(
function (output, key) {
var value = output[key];
if (value instanceof Primitive) {
var tmp = input[value];
if (typeof tmp === 'object' && !parsed.has(tmp)) {
parsed.add(tmp);
output[key] = revive(input, parsed, tmp);
} else {
output[key] = tmp;
}
}
return output;
},
output
);
}
function set(known, input, value) {
var index = Primitive(input.push(value) - 1);
known.set(value, index);
return index;
}
function primitives(value) {
return value instanceof Primitive ? Primitive(value) : value;
}
function Primitives(key, value) {
return typeof value === primitive ? new Primitive(value) : value;
}
}(String, 'string'));
const parse = Flatted.parse;
const stringify = Flatted.stringify;
/*
* Copyright 2017 The boardgame.io Authors
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
var MAKE_MOVE = 'MAKE_MOVE';
var GAME_EVENT = 'GAME_EVENT';
var REDO = 'REDO';
var RESET = 'RESET';
var SYNC = 'SYNC';
var UNDO = 'UNDO';
var UPDATE = 'UPDATE';
// Inlined version of Alea from https://github.com/davidbau/seedrandom.
/*
* Copyright 2015 David Bau.
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software
* and associated documentation files (the "Software"),
* to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
function Alea(seed) {
var me = this,
mash = Mash();
me.next = function () {
var t = 2091639 * me.s0 + me.c * 2.3283064365386963e-10; // 2^-32
me.s0 = me.s1;
me.s1 = me.s2;
return me.s2 = t - (me.c = t | 0);
}; // Apply the seeding algorithm from Baagoe.
me.c = 1;
me.s0 = mash(' ');
me.s1 = mash(' ');
me.s2 = mash(' ');
me.s0 -= mash(seed);
if (me.s0 < 0) {
me.s0 += 1;
}
me.s1 -= mash(seed);
if (me.s1 < 0) {
me.s1 += 1;
}
me.s2 -= mash(seed);
if (me.s2 < 0) {
me.s2 += 1;
}
mash = null;
}
function copy(f, t) {
t.c = f.c;
t.s0 = f.s0;
t.s1 = f.s1;
t.s2 = f.s2;
return t;
}
function Mash() {
var n = 0xefc8249d;
var mash = function mash(data) {
data = data.toString();
for (var i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
var h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
return mash;
}
function alea(seed, opts) {
var xg = new Alea(seed),
state = opts && opts.state,
prng = xg.next;
prng.quick = prng;
if (state) {
if (_typeof(state) == 'object') copy(state, xg);
prng.state = function () {
return copy(xg, {});
};
}
return prng;
}
/**
* Random
*
* Calls that require a pseudorandom number generator.
* Uses a seed from ctx, and also persists the PRNG
* state in ctx so that moves can stay pure.
*/
var Random =
/*#__PURE__*/
function () {
/**
* constructor
* @param {object} ctx - The ctx object to initialize from.
*/
function Random(ctx) {
_classCallCheck(this, Random);
// If we are on the client, the seed is not present.
// Just use a temporary seed to execute the move without
// crashing it. The move state itself is discarded,
// so the actual value doesn't matter.
this.state = ctx._random || {
seed: '0'
};
}
/**
* Updates ctx with the PRNG state.
* @param {object} ctx - The ctx object to update.
*/
_createClass(Random, [{
key: "update",
value: function update(state) {
var ctx = _objectSpread({}, state.ctx, {
_random: this.state
});
return _objectSpread({}, state, {
ctx: ctx
});
}
/**
* Attaches the Random API to ctx.
* @param {object} ctx - The ctx object to attach to.
*/
}, {
key: "attach",
value: function attach(ctx) {
return _objectSpread({}, ctx, {
random: this._api()
});
}
/**
* Generate a random number.
*/
}, {
key: "_random",
value: function _random() {
var R = this.state;
var fn;
if (R.prngstate === undefined) {
// No call to a random function has been made.
fn = new alea(R.seed, {
state: true
});
} else {
fn = new alea('', {
state: R.prngstate
});
}
var number = fn();
this.state = _objectSpread({}, R, {
prngstate: fn.state()
});
return number;
}
}, {
key: "_api",
value: function _api() {
var random = this._random.bind(this);
var SpotValue = {
D4: 4,
D6: 6,
D8: 8,
D10: 10,
D12: 12,
D20: 20
}; // Generate functions for predefined dice values D4 - D20.
var predefined = {};
var _loop = function _loop(key) {
var spotvalue = SpotValue[key];
predefined[key] = function (diceCount) {
if (diceCount === undefined) {
return Math.floor(random() * spotvalue) + 1;
} else {
return _toConsumableArray(new Array(diceCount).keys()).map(function () {
return Math.floor(random() * spotvalue) + 1;
});
}
};
};
for (var key in SpotValue) {
_loop(key);
}
return _objectSpread({}, predefined, {
/**
* Roll a die of specified spot value.
*
* @param {number} spotvalue - The die dimension (default: 6).
* @param {number} diceCount - number of dice to throw.
* if not defined, defaults to 1 and returns the value directly.
* if defined, returns an array containing the random dice values.
*/
Die: function Die(spotvalue, diceCount) {
if (spotvalue === undefined) {
spotvalue = 6;
}
if (diceCount === undefined) {
return Math.floor(random() * spotvalue) + 1;
} else {
return _toConsumableArray(new Array(diceCount).keys()).map(function () {
return Math.floor(random() * spotvalue) + 1;
});
}
},
/**
* Generate a random number between 0 and 1.
*/
Number: function Number() {
return random();
},
/**
* Shuffle an array.
*
* @param {Array} deck - The array to shuffle. Does not mutate
* the input, but returns the shuffled array.
*/
Shuffle: function Shuffle(deck) {
var clone = deck.slice(0);
var srcIndex = deck.length;
var dstIndex = 0;
var shuffled = new Array(srcIndex);
while (srcIndex) {
var randIndex = srcIndex * random() | 0;
shuffled[dstIndex++] = clone[randIndex];
clone[randIndex] = clone[--srcIndex];
}
return shuffled;
}
});
}
}]);
return Random;
}();
/**
* Removes the attached Random api from ctx.
*
* @param {object} ctx - The ctx object with the Random API attached.
* @returns {object} A plain ctx object without the Random API.
*/
Random.detach = function (ctx) {
var random = ctx.random,
rest = _objectWithoutProperties(ctx, ["random"]); // eslint-disable-line no-unused-vars
return rest;
};
/**
* Generates a new seed from the current date / time.
*/
Random.seed = function () {
return (+new Date()).toString(36).slice(-10);
};
/*
* Copyright 2017 The boardgame.io Authors
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
/**
* Generate an automatic game event that is a side-effect of a move.
* @param {string} type - The event type.
* @param {Array} args - Additional arguments.
* @param {string} playerID - The ID of the player making this action.
* @param {string} credentials - (optional) The credentials for the player making this action.
*/
var automaticGameEvent = function automaticGameEvent(type, args, playerID, credentials) {
return {
type: GAME_EVENT,
payload: {
type: type,
args: args,
playerID: playerID,
credentials: credentials
},
automatic: true
};
};
/**
* Events
*/
var Events =
/*#__PURE__*/
function () {
function Events(flow, playerID) {
_classCallCheck(this, Events);
this.flow = flow;
this.playerID = playerID;
this.dispatch = [];
}
/**
* Attaches the Events API to ctx.
* @param {object} ctx - The ctx object to attach to.
*/
_createClass(Events, [{
key: "attach",
value: function attach(ctx) {
var _this = this;
var events = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
var _loop = function _loop() {
var key = _step.value;
events[key] = function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this.dispatch.push({
key: key,
args: args
});
};
};
for (var _iterator = this.flow.eventNames[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
_loop();
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _objectSpread({}, ctx, {
events: events
});
}
/**
* Updates ctx with the triggered events.
* @param {object} state - The state object { G, ctx }.
*/
}, {
key: "update",
value: function update$$1(state) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = this.dispatch[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var item = _step2.value;
var action = automaticGameEvent(item.key, item.args, this.playerID);
state = _objectSpread({}, state, this.flow.processGameEvent(state, action));
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return state;
}
}]);
return Events;
}();
/**
* Detaches the Events API from ctx.
* @param {object} ctx - The ctx object to strip.
*/
Events.detach = function (ctx) {
var events = ctx.events,
rest = _objectWithoutProperties(ctx, ["events"]); // eslint-disable-line no-unused-vars
return rest;
};
var _typeof$1 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var defineProperty = function (obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
};
var NOTHING = typeof Symbol !== "undefined" ? Symbol("immer-nothing") : defineProperty({}, "immer-nothing", true);
var DRAFT_STATE = typeof Symbol !== "undefined" ? Symbol("immer-state") : "__$immer_state";
function isDraft(value) {
return !!value && !!value[DRAFT_STATE];
}
function isDraftable(value) {
if (!value) return false;
if ((typeof value === "undefined" ? "undefined" : _typeof$1(value)) !== "object") return false;
if (Array.isArray(value)) return true;
var proto = Object.getPrototypeOf(value);
return proto === null || proto === Object.prototype;
}
var assign = Object.assign || function assign(target, value) {
for (var key in value) {
if (has(value, key)) {
target[key] = value[key];
}
}
return target;
};
function shallowCopy(value) {
if (Array.isArray(value)) return value.slice();
var target = value.__proto__ === undefined ? Object.create(null) : {};
return assign(target, value);
}
function each(value, cb) {
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) {
cb(i, value[i], value);
}
} else {
for (var key in value) {
cb(key, value[key], value);
}
}
}
function has(thing, prop) {
return Object.prototype.hasOwnProperty.call(thing, prop);
}
function is(x, y) {
// From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
}
function generatePatches(state, basePath, patches, inversePatches) {
Array.isArray(state.base) ? generateArrayPatches(state, basePath, patches, inversePatches) : generateObjectPatches(state, basePath, patches, inversePatches);
}
function generateArrayPatches(state, basePath, patches, inversePatches) {
var base = state.base,
copy = state.copy,
assigned = state.assigned;
var minLength = Math.min(base.length, copy.length);
// Look for replaced indices.
for (var i = 0; i < minLength; i++) {
if (assigned[i] && base[i] !== copy[i]) {
var path = basePath.concat(i);
patches.push({ op: "replace", path: path, value: copy[i] });
inversePatches.push({ op: "replace", path: path, value: base[i] });
}
}
// Did the array expand?
if (minLength < copy.length) {
for (var _i = minLength; _i < copy.length; _i++) {
patches.push({
op: "add",
path: basePath.concat(_i),
value: copy[_i]
});
}
inversePatches.push({
op: "replace",
path: basePath.concat("length"),
value: base.length
});
}
// ...or did it shrink?
else if (minLength < base.length) {
patches.push({
op: "replace",
path: basePath.concat("length"),
value: copy.length
});
for (var _i2 = minLength; _i2 < base.length; _i2++) {
inversePatches.push({
op: "add",
path: basePath.concat(_i2),
value: base[_i2]
});
}
}
}
function generateObjectPatches(state, basePath, patches, inversePatches) {
var base = state.base,
copy = state.copy;
each(state.assigned, function (key, assignedValue) {
var origValue = base[key];
var value = copy[key];
var op = !assignedValue ? "remove" : key in base ? "replace" : "add";
if (origValue === base && op === "replace") return;
var path = basePath.concat(key);
patches.push(op === "remove" ? { op: op, path: path } : { op: op, path: path, value: value });
inversePatches.push(op === "add" ? { op: "remove", path: path } : op === "remove" ? { op: "add", path: path, value: origValue } : { op: "replace", path: path, value: origValue });
});
}
function applyPatches(draft, patches) {
for (var i = 0; i < patches.length; i++) {
var patch = patches[i];
var path = patch.path;
if (path.length === 0 && patch.op === "replace") {
draft = patch.value;
} else {
var base = draft;
for (var _i3 = 0; _i3 < path.length - 1; _i3++) {
base = base[path[_i3]];
if (!base || (typeof base === "undefined" ? "undefined" : _typeof$1(base)) !== "object") throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")); // prettier-ignore
}
var key = path[path.length - 1];
switch (patch.op) {
case "replace":
case "add":
// TODO: add support is not extensive, it does not support insertion or `-` atm!
base[key] = patch.value;
break;
case "remove":
if (Array.isArray(base)) {
if (key !== base.length - 1) throw new Error("Only the last index of an array can be removed, index: " + key + ", length: " + base.length); // prettier-ignore
base.length -= 1;
} else {
delete base[key];
}
break;
default:
throw new Error("Unsupported patch operation: " + patch.op);
}
}
}
return draft;
}
// @ts-check
var descriptors = {};
// For nested produce calls:
var scopes = [];
var currentScope = function currentScope() {
return scopes[scopes.length - 1];
};
function willFinalize(result, baseDraft, needPatches) {
var scope = currentScope();
scope.forEach(function (state) {
return state.finalizing = true;
});
if (result === undefined || result === baseDraft) {
if (needPatches) markChangesRecursively(baseDraft);
// This is faster when we don't care about which attributes changed.
markChangesSweep(scope);
}
}
function createDraft(base, parent) {
var draft = void 0;
if (isDraft(base)) {
var _state = base[DRAFT_STATE];
// Avoid creating new drafts when copying.
_state.finalizing = true;
draft = shallowCopy(_state.draft);
_state.finalizing = false;
} else {
draft = shallowCopy(base);
}
each(base, function (prop) {
Object.defineProperty(draft, "" + prop, createPropertyProxy("" + prop));
});
// See "proxy.js" for property documentation.
var state = {
scope: parent ? parent.scope : currentScope(),
modified: false,
finalizing: false, // es5 only
finalized: false,
assigned: {},
parent: parent,
base: base,
draft: draft,
copy: null,
revoke: revoke,
revoked: false // es5 only
};
createHiddenProperty(draft, DRAFT_STATE, state);
state.scope.push(state);
return draft;
}
function revoke() {
this.revoked = true;
}
function source(state) {
return state.copy || state.base;
}
function _get$1(state, prop) {
assertUnrevoked(state);
var value = source(state)[prop];
// Drafts are only created for proxyable values that exist in the base state.
if (!state.finalizing && value === state.base[prop] && isDraftable(value)) {
prepareCopy(state);
return state.copy[prop] = createDraft(value, state);
}
return value;
}
function _set$1(state, prop, value) {
assertUnrevoked(state);
state.assigned[prop] = true;
if (!state.modified) {
if (is(source(state)[prop], value)) return;
markChanged(state);
prepareCopy(state);
}
state.copy[prop] = value;
}
function markChanged(state) {
if (!state.modified) {
state.modified = true;
if (state.parent) markChanged(state.parent);
}
}
function prepareCopy(state) {
if (!state.copy) state.copy = shallowCopy(state.base);
}
function createPropertyProxy(prop) {
return descriptors[prop] || (descriptors[prop] = {
configurable: true,
enumerable: true,
get: function get$$1() {
return _get$1(this[DRAFT_STATE], prop);
},
set: function set$$1(value) {
_set$1(this[DRAFT_STATE], prop, value);
}
});
}
function assertUnrevoked(state) {
if (state.revoked === true) throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(state.copy || state.base));
}
// This looks expensive, but only proxies are visited, and only objects without known changes are scanned.
function markChangesSweep(scope) {
// The natural order of drafts in the `scope` array is based on when they
// were accessed. By processing drafts in reverse natural order, we have a
// better chance of processing leaf nodes first. When a leaf node is known to
// have changed, we can avoid any traversal of its ancestor nodes.
for (var i = scope.length - 1; i >= 0; i--) {
var state = scope[i];
if (state.modified === false) {
if (Array.isArray(state.base)) {
if (hasArrayChanges(state)) markChanged(state);
} else if (hasObjectChanges(state)) markChanged(state);
}
}
}
function markChangesRecursively(object) {
if (!object || (typeof object === "undefined" ? "undefined" : _typeof$1(object)) !== "object") return;
var state = object[DRAFT_STATE];
if (!state) return;
var base = state.base,
draft = state.draft,
assigned = state.assigned;
if (!Array.isArray(object)) {
// Look for added keys.
Object.keys(draft).forEach(function (key) {
// The `undefined` check is a fast path for pre-existing keys.
if (base[key] === undefined && !has(base, key)) {
assigned[key] = true;
markChanged(state);
} else if (!assigned[key]) {
// Only untouched properties trigger recursion.
markChangesRecursively(draft[key]);
}
});
// Look for removed keys.
Object.keys(base).forEach(function (key) {
// The `undefined` check is a fast path for pre-existing keys.
if (draft[key] === undefined && !has(draft, key)) {
assigned[key] = false;
markChanged(state);
}
});
} else if (hasArrayChanges(state)) {
markChanged(state);
assigned.length = true;
if (draft.length < base.length) {
for (var i = draft.length; i < base.length; i++) {
assigned[i] = false;
}
} else {
for (var _i = base.length; _i < draft.length; _i++) {
assigned[_i] = true;
}
}
for (var _i2 = 0; _i2 < draft.length; _i2++) {
// Only untouched indices trigger recursion.
if (assigned[_i2] === undefined) markChangesRecursively(draft[_i2]);
}
}
}
function hasObjectChanges(state) {
var base = state.base,
draft = state.draft;
// Search for added keys. Start at the back, because non-numeric keys
// are ordered by time of definition on the object.
var keys = Object.keys(draft);
for (var i = keys.length - 1; i >= 0; i--) {
// The `undefined` check is a fast path for pre-existing keys.
if (base[keys[i]] === undefined && !has(base, keys[i])) {
return true;
}
}
// Since no keys have been added, we can compare lengths to know if an
// object has been deleted.
return keys.length !== Object.keys(base).length;
}
function hasArrayChanges(state) {
var draft = state.draft;
if (draft.length !== state.base.length) return true;
// See #116
// If we first shorten the length, our array interceptors will be removed.
// If after that new items are added, result in the same original length,
// those last items will have no intercepting property.
// So if there is no own descriptor on the last position, we know that items were removed and added
// N.B.: splice, unshift, etc only shift values around, but not prop descriptors, so we only have to check
// the last one
var descriptor = Object.getOwnPropertyDescriptor(draft, draft.length - 1);
// descriptor can be null, but only for newly created sparse arrays, eg. new Array(10)
if (descriptor && !descriptor.get) return true;
// For all other cases, we don't have to compare, as they would have been picked up by the index setters
return false;
}
function createHiddenProperty(target, prop, value) {
Object.defineProperty(target, prop, {
value: value,
enumerable: false,
writable: true
});
}
var legacyProxy = Object.freeze({
scopes: scopes,
currentScope: currentScope,
willFinalize: willFinalize,
createDraft: createDraft
});
// @ts-check
// For nested produce calls:
var scopes$1 = [];
var currentScope$1 = function currentScope() {
return scopes$1[scopes$1.length - 1];
};
// Do nothing before being finalized.
function willFinalize$1() {}
function createDraft$1(base, parent) {
var state = {
// Track which produce call this is associated with.
scope: parent ? parent.scope : currentScope$1(),
// True for both shallow and deep changes.
modified: false,
// Used during finalization.
finalized: false,
// Track which properties have been assigned (true) or deleted (false).
assigned: {},
// The parent draft state.
parent: parent,
// The base state.
base: base,
// The base proxy.
draft: null,
// Any property proxies.
drafts: {},
// The base copy with any updated values.
copy: null,
// Called by the `produce` function.
revoke: null
};
var _ref = Array.isArray(base) ? Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps),
revoke = _ref.revoke,
proxy = _ref.proxy;
state.draft = proxy;
state.revoke = revoke;
state.scope.push(state);
return proxy;
}
var objectTraps = {
get: get$1,
has: function has$$1(target, prop) {
return prop in source$1(target);
},
ownKeys: function ownKeys(target) {
return Reflect.ownKeys(source$1(target));
},
set: set$1,
deleteProperty: deleteProperty,
getOwnPropertyDescriptor: getOwnPropertyDescriptor,
defineProperty: defineProperty$1,
setPrototypeOf: function setPrototypeOf() {
throw new Error("Immer does not support `setPrototypeOf()`.");
}
};
var arrayTraps = {};
each(objectTraps, function (key, fn) {
arrayTraps[key] = function () {
arguments[0] = arguments[0][0];
return fn.apply(this, arguments);
};
});
arrayTraps.deleteProperty = function (state, prop) {
if (isNaN(parseInt(prop))) throw new Error("Immer does not support deleting properties from arrays: " + prop);
return objectTraps.deleteProperty.call(this, state[0], prop);
};
arrayTraps.set = function (state, prop, value) {
if (prop !== "length" && isNaN(parseInt(prop))) throw new Error("Immer does not support setting non-numeric properties on arrays: " + prop);
return objectTraps.set.call(this, state[0], prop, value);
};
function source$1(state) {
return state.copy || state.base;
}
function get$1(state, prop) {
if (prop === DRAFT_STATE) return state;
var drafts = state.drafts;
// Check for existing draft in unmodified state.
if (!state.modified && has(drafts, prop)) {
return drafts[prop];
}
var value = source$1(state)[prop];
if (state.finalized || !isDraftable(value)) return value;
// Check for existing draft in modified state.
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== state.base[prop]) return value;
// Store drafts on the copy (when one exists).
drafts = state.copy;
}
return drafts[prop] = createDraft$1(value, state);
}
function set$1(state, prop, value) {
if (!state.modified) {
// Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
var isUnchanged = value ? is(state.base[prop], value) || value === state.drafts[prop] : is(state.base[prop], value) && prop in state.base;
if (isUnchanged) return true;
markChanged$1(state);
}
state.assigned[prop] = true;
state.copy[prop] = value;
return true;
}
function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (state.base[prop] !== undefined || prop in state.base) {
state.assigned[prop] = false;
markChanged$1(state);
}
if (state.copy) delete state.copy[prop];
return true;
}
function getOwnPropertyDescriptor(state, prop) {
var owner = state.modified ? state.copy : has(state.drafts, prop) ? state.drafts : state.base;
var descriptor = Reflect.getOwnPropertyDescriptor(owner, prop);
if (descriptor && !(Array.isArray(owner) && prop === "length")) descriptor.configurable = true;
return descriptor;
}
function defineProperty$1() {
throw new Error("Immer does not support defining properties on draft objects.");
}
function markChanged$1(state) {
if (!state.modified) {
state.modified = true;
state.copy = assign(shallowCopy(state.base), state.drafts);
state.drafts = null;
if (state.parent) markChanged$1(state.parent);
}
}
var modernProxy = Object.freeze({
scopes: scopes$1,
currentScope: currentScope$1,
willFinalize: willFinalize$1,
createDraft: createDraft$1
});
function verifyMinified() {}
var configDefaults = {
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
autoFreeze: typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : verifyMinified.name === "verifyMinified",
onAssign: null,
onDelete: null,
onCopy: null
};
var Immer = function () {
function Immer(config) {
classCallCheck(this, Immer);
assign(this, configDefaults, config);
this.setUseProxies(this.useProxies);
this.produce = this.produce.bind(this);
}
createClass(Immer, [{
key: "produce",
value: function produce(base, recipe, patchListener) {
var _this = this;
// curried invocation
if (typeof base === "function" && typeof recipe !== "function") {
var defaultBase = recipe;
recipe = base;
// prettier-ignore
return function () {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var base = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultBase;
return _this.produce(base, function (draft) {
var _recipe;
return (_recipe = recipe).call.apply(_recipe, [draft, draft].concat(args));
});
};
}
// prettier-ignore
{
if (typeof recipe !== "function") throw new Error("if first argument is not a function, the second argument to produce should be a function");
if (patchListener !== undefined && typeof patchListener !== "function") throw new Error("the third argument of a producer should not be set or a function");
}
var result = void 0;
// Only create proxies for plain objects/arrays.
if (!isDraftable(base)) {
result = recipe(base);
if (result === undefined) return base;
}
// See #100, don't nest producers
else if (isDraft(base)) {
result = recipe.call(base, base);
if (result === undefined) return base;
}
// The given value must be proxied.
else {
this.scopes.push([]);
var baseDraft = this.createDraft(base);
try {
result = recipe.call(baseDraft, baseDraft);
this.willFinalize(result, baseDraft, !!patchListener);
// Never generate patches when no listener exists.
var patches = patchListener && [],
inversePatches = patchListener && [];
// Finalize the modified draft...
if (result === undefined || result === baseDraft) {
result = this.finalize(baseDraft, [], patches, inversePatches);
}
// ...or use a replacement value.
else {
// Users must never modify the draft _and_ return something else.
if (baseDraft[DRAFT_STATE].modified) throw new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft."); // prettier-ignore
// Finalize the replacement in case it contains (or is) a subset of the draft.
if (isDraftable(result)) result = this.finalize(result);
if (patchListener) {
patches.push({
op: "replace",
path: [],
value: result
});
inversePatches.push({
op: "replace",
path: [],
value: base
});
}
}
} finally {
this.currentScope().forEach(function (state) {
return state.revoke();
});
this.scopes.pop();
}
patchListener && patchListener(patches, inversePatches);
}
// Normalize the result.
return result === NOTHING ? undefined : result;
}
}, {
key: "setAutoFreeze",
value: function setAutoFreeze(value) {
this.autoFreeze = value;
}
}, {
key: "setUseProxies",
value: function setUseProxies(value) {
this.useProxies = value;
assign(this, value ? modernProxy : legacyProxy);
}
/**
* @internal
* Finalize a draft, returning either the unmodified base state or a modified
* copy of the base state.
*/
}, {
key: "finalize",
value: function finalize(draft, path, patches, inversePatches) {
var state = draft[DRAFT_STATE];
if (!state) {
if (Object.isFrozen(draft)) return draft;
return this.finalizeTree(draft);
}
// Never finalize drafts owned by an outer scope.
if (state.scope !== this.currentScope()) {
return draft;
}
if (!state.modified) return state.base;
if (!state.finalized) {
state.finalized = true;
this.finalizeTree(state.draft, path, patches, inversePatches);
if (this.onDelete) {
var assigned = state.assigned;
for (var prop in assigned) {
assigned[prop] || this.onDelete(state, prop);
}
}
if (this.onCopy) this.onCopy(state);
// Nested producers must never auto-freeze their result,
// because it may contain drafts from parent producers.
if (this.autoFreeze && this.scopes.length === 1) {
Object.freeze(state.copy);
}
if (patches) generatePatches(state, path, patches, inversePatches);
}
return state.copy;
}
/**
* @internal
* Finalize all drafts in the given state tree.
*/
}, {
key: "finalizeTree",
value: function finalizeTree(root, path, patches, inversePatches) {
var _this2 = this;
var state = root[DRAFT_STATE];
if (state) {
root = this.useProxies ? state.copy : state.copy = shallowCopy(state.draft);
}
var onAssign = this.onAssign;
var finalizeProperty = function finalizeProperty(prop, value, parent) {
// Only `root` can be a draft in here.
var inDraft = !!state && parent === root;
if (isDraft(value)) {
// prettier-ignore
parent[prop] = value =
// Patches are never generated for assigned properties.
patches && inDraft && !state.assigned[prop] ? _this2.finalize(value, path.concat(prop), patches, inversePatches) : _this2.finalize(value);
// Unchanged drafts are ignored.
if (inDraft && value === state.base[prop]) return;
}
// Unchanged draft properties are ignored.
else if (inDraft && is(value, state.base[prop])) {
return;
}
// Search new objects for unfinalized drafts. Frozen objects should never contain drafts.
else if (isDraftable(value) && !Object.isFrozen(value)) {
each(value, finalizeProperty);
}
if (inDraft && onAssign) {
onAssign(state, prop, value);
}
};
each(root, finalizeProperty);
return root;
}
}]);
return Immer;
}();
var immer = new Immer();
/**
* The `produce` function takes a value and a "recipe function" (whose
* return value often depends on the base state). The recipe function is
* free to mutate its first argument however it wants. All mutations are
* only ever applied to a __copy__ of the base state.
*
* Pass only a function to create a "curried producer" which relieves you
* from passing the recipe function every time.
*
* Only plain objects and arrays are made mutable. All other objects are
* considered uncopyable.
*
* Note: This function is __bound__ to its `Immer` instance.
*
* @param {any} base - the initial state
* @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
* @param {Function} patchListener - optional function that will be called with all the patches produced here
* @returns {any} a new state, or the initial state if nothing was modified
*/
var produce = immer.produce;
/**
* Apply an array of Immer patches to the first argument.
*
* This function is a producer, which means copy-on-write is in effect.
*/
var applyPatches$1 = produce(applyPatches);
/*
* Copyright 2018 The boardgame.io Authors
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
/**
* Plugin that allows using Immer to make immutable changes
* to G by just mutating it.
*/
var PluginImmer = {
fnWrap: function fnWrap(move) {
return produce(move);
}
};
/**
* List of plugins that are always added.
*/
var DEFAULT_PLUGINS = [PluginImmer];
var G = {
/**
* Applies the provided plugins to G during game setup.
*
* @param {object} G - The G object.
* @param {object} ctx - The ctx object.
* @param {object} game - The game object.
*/
setup: function setup(G, ctx, game) {
[].concat(DEFAULT_PLUGINS, _toConsumableArray(game.plugins)).filter(function (plugin) {
return plugin.G !== undefined;
}).filter(function (plugin) {
return plugin.G.setup !== undefined;
}).forEach(function (plugin) {
G = plugin.G.setup(G, ctx, game);
});
return G;
},
/**
* Applies the provided plugins to G during the beginning of the phase.
*
* @param {object} G - The G object.
* @param {object} ctx - The ctx object.
* @param {object} game - The game object.
*/
onPhaseBegin: function onPhaseBegin(G, ctx, game) {
[].concat(DEFAULT_PLUGINS, _toConsumableArray(game.plugins)).filter(function (plugin) {
return plugin.G !== undefined;
}).filter(function (plugin) {
return plugin.G.onPhaseBegin !== undefined;
}).forEach(function (plugin) {
G = plugin.G.onPhaseBegin(G, ctx, game);
});
return G;
}
};
var ctx = {
/**
* Applies the provided plugins to ctx during game setup.
*
* @param {object} ctx - The ctx object.
* @param {object} game - The game object.
*/
setup: function setup(ctx, game) {
[].concat(DEFAULT_PLUGINS, _toConsumableArray(game.plugins)).filter(function (plugin) {
return plugin.ctx !== undefined;
}).filter(function (plugin) {
return plugin.ctx.setup !== undefined;
}).forEach(function (plugin) {
ctx = plugin.ctx.setup(ctx, game);
});
return ctx;
},
/**
* Applies the provided plugins to ctx during the beginning of the phase.
*
* @param {object} ctx - The ctx object.
* @param {object} game - The game object.
*/
onPhaseBegin: function onPhaseBegin(ctx, game) {
[].concat(DEFAULT_PLUGINS, _toConsumableArray(game.plugins)).filter(function (plugin) {
return plugin.ctx !== undefined;
}).filter(function (plugin) {
return plugin.ctx.onPhaseBegin !== undefined;
}).forEach(function (plugin) {
ctx = plugin.ctx.onPhaseBegin(ctx, game);
});
return ctx;