+/**
+ * Returns whether the provided value is spreadable during array
+ * concatenation.
+ *
+ * This is also used to determine which things should be treated as
+ * collections.
+ */
+export const isConcatSpreadableObject = ($) => {
+ if (type($) !== "object") {
+ // The provided value is not an object.
+ return false;
+ } else {
+ // The provided value is an object.
+ const spreadable = $[IS_CONCAT_SPREADABLE];
+ return spreadable !== UNDEFINED ? !!spreadable : isArray($);
+ }
+};
+
+/**
+ * Returns whether the provided value is an extensible object.
+ *
+ * ※ This function returns false for nonobjects.
+ *
+ * ※ This is effectively an alias for `Object.isExtensible`.
+ */
+export const isExtensibleObject = (O) => isExtensible(O);
+
+export const {
+ /**
+ * Returns whether the provided value is a property descriptor record
+ * as created by `toPropertyDescriptor`.
+ *
+ * ※ This function is provided to enable inspection of whether an
+ * object uses the property descriptor record proxy implementation,
+ * not as a general test of whether an object satisfies the
+ * requirements for property descriptors. In most cases, a more
+ * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
+ * `isGenericDescriptor`, is preferrable.
+ */
+ isPropertyDescriptorRecord,
+
+ /**
+ * Converts the provided value to a property descriptor record.
+ *
+ * ※ The prototype of a property descriptor record is always `null`.
+ *
+ * ※ Actually constructing a property descriptor object using this
+ * class is only necessary if you need strict guarantees about the
+ * types of its properties; the resulting object is proxied to ensure
+ * the types match what one would expect from composing
+ * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
+ * specification.
+ */
+ toPropertyDescriptorRecord,
+} = (() => {
+ const proxyConstructor = Proxy;
+ const { add: weakSetAdd, has: weakSetHas } = WeakSet.prototype;
+ const propertyDescriptorRecords = new WeakSet();
+ const coercePropertyDescriptorValue = (P, V) => {
+ switch (P) {
+ case "configurable":
+ case "enumerable":
+ case "writable":
+ return !!V;
+ case "value":
+ return V;
+ case "get":
+ if (V !== undefined && typeof V !== "function") {
+ throw new TypeError(
+ "Piscēs: Getters must be callable.",
+ );
+ } else {
+ return V;
+ }
+ case "set":
+ if (V !== undefined && typeof V !== "function") {
+ throw new TypeError(
+ "Piscēs: Setters must be callable.",
+ );
+ } else {
+ return V;
+ }
+ default:
+ return V;
+ }
+ };
+ const propertyDescriptorProxyHandler = objectFreeze(
+ assign(
+ create(null),
+ {
+ defineProperty(O, P, Desc) {
+ if (
+ P === "configurable" || P === "enumerable" ||
+ P === "writable" || P === "value" ||
+ P === "get" || P === "set"
+ ) {
+ // P is a property descriptor attribute.
+ const desc = assign(objectCreate(null), Desc);
+ if ("get" in desc || "set" in desc) {
+ // Desc is an accessor property descriptor.
+ throw new TypeError(
+ "Piscēs: Property descriptor attributes must be data properties.",
+ );
+ } else if ("value" in desc || !(P in O)) {
+ // Desc has a value or P does not already exist on O.
+ desc.value = coercePropertyDescriptorValue(
+ P,
+ desc.value,
+ );
+ } else {
+ // Desc is not an accessor property descriptor and has no
+ // value, but an existing value is present on O.
+ /* do nothing */
+ }
+ const isAccessorDescriptor = "get" === P || "set" === P ||
+ "get" in O || "set" in O;
+ const isDataDescriptor = "value" === P ||
+ "writable" === P ||
+ "value" in O || "writable" in O;
+ if (isAccessorDescriptor && isDataDescriptor) {
+ // Both accessor and data attributes will be present on O
+ // after defining P.
+ throw new TypeError(
+ "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+ );
+ } else {
+ // P can be safely defined on O.
+ return reflectDefineProperty(O, P, desc);
+ }
+ } else {
+ // P is not a property descriptor attribute.
+ return reflectDefineProperty(O, P, Desc);
+ }
+ },
+ setPrototypeOf(O, V) {
+ if (V !== null) {
+ // V is not the property descriptor prototype.
+ return false;
+ } else {
+ // V is the property descriptor prototype.
+ return reflectSetPrototypeOf(O, V);
+ }
+ },
+ },
+ ),
+ );
+
+ return {
+ isPropertyDescriptorRecord: ($) =>
+ call(weakSetHas, propertyDescriptorRecords, [$]),
+ toPropertyDescriptorRecord: (Obj) => {
+ if (type(Obj) !== "object") {
+ // The provided value is not an object.
+ throw new TypeError(
+ `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
+ );
+ } else {
+ // The provided value is an object.
+ const desc = create(null);
+ if ("enumerable" in Obj) {
+ // An enumerable property is specified.
+ defineOwnDataProperty(desc, "enumerable", !!Obj.enumerable);
+ } else {
+ // An enumerable property is not specified.
+ /* do nothing */
+ }
+ if ("configurable" in Obj) {
+ // A configurable property is specified.
+ defineOwnDataProperty(
+ desc,
+ "configurable",
+ !!Obj.configurable,
+ );
+ } else {
+ // A configurable property is not specified.
+ /* do nothing */
+ }
+ if ("value" in Obj) {
+ // A value property is specified.
+ defineOwnDataProperty(desc, "value", Obj.value);
+ } else {
+ // A value property is not specified.
+ /* do nothing */
+ }
+ if ("writable" in Obj) {
+ // A writable property is specified.
+ defineOwnDataProperty(desc, "writable", !!Obj.writable);
+ } else {
+ // A writable property is not specified.
+ /* do nothing */
+ }
+ if ("get" in Obj) {
+ // A get property is specified.
+ const getter = Obj.get;
+ if (getter !== UNDEFINED && typeof getter !== "function") {
+ // The getter is not callable.
+ throw new TypeError("Piscēs: Getters must be callable.");
+ } else {
+ // The getter is callable.
+ defineOwnDataProperty(desc, "get", Obj.get);
+ }
+ } else {
+ // A get property is not specified.
+ /* do nothing */
+ }
+ if ("set" in Obj) {
+ // A set property is specified.
+ const setter = Obj.set;
+ if (setter !== UNDEFINED && typeof setter !== "function") {
+ // The setter is not callable.
+ throw new TypeError("Piscēs: Setters must be callable.");
+ } else {
+ // The setter is callable.
+ defineOwnDataProperty(desc, "set", Obj.set);
+ }
+ } else {
+ // A set property is not specified.
+ /* do nothing */
+ }
+ if (
+ ("get" in desc || "set" in desc) &&
+ ("value" in desc || "writable" in desc)
+ ) {
+ // Both accessor and data attributes have been defined.
+ throw new TypeError(
+ "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+ );
+ } else {
+ // The property descriptor is valid.
+ const record = new proxyConstructor(
+ desc,
+ propertyDescriptorProxyHandler,
+ );
+ call(weakSetAdd, propertyDescriptorRecords, [record]);
+ return record;
+ }
+ }
+ },
+ };
+})();
+
+/**
+ * Returns whether the provided value is an unfrozen object.
+ *
+ * ※ This function returns false for nonobjects.
+ *
+ * ※ This is effectively an alias for `!Object.isFrozen`.
+ */
+export const isUnfrozenObject = (O) => !isFrozen(O);
+
+/**
+ * Returns whether the provided value is an unsealed object.
+ *
+ * ※ This function returns false for nonobjects.
+ *
+ * ※ This is effectively an alias for `!Object.isSealed`.
+ */
+export const isUnsealedObject = (O) => !isSealed(O);
+
+/**
+ * Returns the length of the provided arraylike value.
+ *
+ * This can produce larger lengths than can actually be stored in
+ * arrays, because no such restrictions exist on arraylike methods.
+ *
+ * ☡ This function throws if the provided value is not arraylike.
+ */
+export const lengthOfArraylike = ({ length }) => toLength(length);
+
+/**
+ * Returns an array of key~value pairs for the enumerable,
+ * string‐valued property keys on the provided value.
+ *
+ * ※ This is effectively an alias for `Object.entries`.
+ */
+export const namedEntries = (O) => entries(O);
+
+/**
+ * Returns an array of the enumerable, string‐valued property keys on
+ * the provided value.
+ *
+ * ※ This is effectively an alias for `Object.keys`.
+ */
+export const namedKeys = (O) => keys(O);
+
+/**
+ * Returns an array of property values for the enumerable,
+ * string‐valued property keys on the provided value.
+ *
+ * ※ This is effectively an alias for `Object.values`.
+ */
+export const namedValues = (O) => values(O);
+
+/**
+ * Returns a new object with the provided prototype and property
+ * descriptors.
+ *
+ * ※ This is effectively an alias for `Object.create`.
+ */
+export const objectCreate = (O, Properties) => create(O, Properties);
+
+/**
+ * Returns a new object with property keys and values from the provided
+ * iterable value.
+ *
+ * ※ This is effectively an alias for `Object.fromEntries`.
+ */
+export const objectFromEntries = (iterable) => fromEntries(iterable);
+
+/**
+ * Marks the provided object as non·extensible, and returns the
+ * object.
+ *
+ * ※ This is effectively an alias for `Object.preventExtensions`.
+ */
+export const preventExtensions = (O) => objectPreventExtensions(O);
+
+/**
+ * Marks the provided object as non·extensible and marks all its
+ * properties as nonconfigurable, and returns the object.
+ *
+ * ※ This is effectively an alias for `Object.seal`.
+ */
+export const seal = (O) => objectSeal(O);
+
+/**
+ * Sets the provided property key to the provided value on the provided
+ * object and returns the object.
+ *
+ * ※ This function differs from `Reflect.set` in that it throws if the
+ * setting is unsuccessful.
+ *
+ * ☡ This function throws if the first argument is not an object.
+ */
+export const setPropertyValue = (O, P, V, Receiver = O) => {
+ if (type(O) !== "object") {
+ throw new TypeError(
+ `Piscēs: Tried to set property but provided value was not an object: ${V}`,
+ );
+ } else if (!set(O, P, V, Receiver)) {
+ throw new TypeError(
+ `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
+ );
+ } else {
+ return O;
+ }
+};
+
+/**
+ * Sets the values of the enumerable own properties of the provided
+ * additional objects on the provided values.
+ *
+ * ※ This is effectively an alias for `Object.assign`.
+ */
+export const setPropertyValues = (target, source, ...sources) => {
+ const to = toObject(target);
+ for (let i = -1; i < sources.length; ++i) {
+ // Iterate over each source and set the appropriate property
+ // values.
+ const nextSource = i === -1 ? source : sources[i];
+ if (nextSource != null) {
+ // The current source is not nullish; handle its own properties.
+ const from = toObject(nextSource);
+ const keys = ownKeys(from);
+ for (let k = 0; k < keys.length; ++k) {
+ // Iterate over each key in the current source and set it in
+ // the target object if it is enumerable.
+ const nextKey = keys[k];
+ const desc = reflectGetOwnPropertyDescriptor(from, nextKey);
+ if (desc !== UNDEFINED && desc.enumerable) {
+ // The current key is present and enumerable; set it to its
+ // corresponding value.
+ const propValue = from[nextKey];
+ to[nextKey] = propValue;
+ } else {
+ // The current key is not present or not enumerable.
+ /* do nothing */
+ }
+ }
+ } else {
+ // The current source is nullish.
+ /* do nothing */
+ }
+ }
+ return to;
+};
+
+/**
+ * Sets the prototype of the provided object to the provided value and
+ * returns the object.
+ *
+ * ※ This is effectively an alias for `Object.setPrototypeOf`, but it
+ * won’t throw when setting the prototype of a primitive to its current
+ * value.
+ */
+export const setPrototype = (O, proto) => {
+ const obj = toObject(O);
+ if (O === obj) {
+ // The provided value is an object; set its prototype normally.
+ return setPrototypeOf(O, proto);
+ } else {
+ // The provided value is not an object; attempt to set the
+ // prototype on a coerced version with extensions prevented, then
+ // return the provided value.
+ //
+ // This will throw if the given prototype does not match the
+ // existing one on the coerced object.
+ setPrototypeOf(objectPreventExtensions(obj), proto);
+ return O;
+ }