+const {
+ /**
+ * A `Tag` constructor function.
+ *
+ * This class extends the identity function, meaning that the object
+ * provided as the constructor is used verbatim (with new private
+ * fields added).
+ *
+ * ※ The instance methods of this class are provided as static
+ * methods on the superclass which all `Tag` constructors inherit
+ * from.
+ *
+ * ※ This class is not exposed.
+ */
+ TagConstructor,
+
+ /**
+ * The exposed constructor function from which all `Tag` constructors
+ * inherit.
+ *
+ * ☡ This constructor always throws.
+ */
+ TagSuper,
+} = (() => {
+ const tagConstructorBehaviours = Object.create(null);
+ return {
+ TagConstructor: class extends identity {
+ /**
+ * The `TagSystem` used for `Tag`s constructed by this
+ * constructor.
+ */
+ #system;
+
+ /** The `Storage` managed by this constructor’s `TagSystem`. */
+ #storage;
+
+ /** The schema in use for this constructor. */
+ #schema;
+
+ /**
+ * Constructs a new `Tag` constructor by adding the appropriate
+ * private fields to the provided constructor, setting its
+ * prototype, and then returning it.
+ *
+ * ※ This constructor does not modify the `name` or `prototype`
+ * properties of the provided constructor.
+ *
+ * ※ See `Tag.For`, where this constructor is used.
+ */
+ constructor(constructor, system, storage, schema) {
+ super(constructor);
+ Object.setPrototypeOf(this, TagSuper);
+ this.#system = system;
+ this.#storage = storage;
+ this.#schema = schema;
+ }
+
+ static {
+ // Define the superclass constructor which all `Tag`
+ // constructors will inherit from.
+ const superclass = tagConstructorBehaviours.TagSuper =
+ function Tag() {
+ throw new TypeError("Tags must belong to a System.");
+ };
+ const { prototype: methods } = this;
+ delete methods.constructor;
+ Object.defineProperty(superclass, "prototype", {
+ configurable: false,
+ enumerable: false,
+ value: Tag.prototype,
+ writable: false,
+ });
+ Object.defineProperties(
+ superclass,
+ Object.getOwnPropertyDescriptors(methods),
+ );
+ }
+
+ /**
+ * Yields the tags in the `TagSystem` associated with this
+ * constructor.
+ */
+ *all() {
+ const system = this.#system;
+ const storage = this.#storage;
+ for (const instance of storage.values()) {
+ // Iterate over the entries and yield the ones which are
+ // `Tag`s in this `TagSystem`.
+ if (Tag.getSystem(instance) == system) {
+ // The current instance is a `Tag` in this `TagSystem`.
+ yield instance;
+ } else {
+ // The current instance is not a `Tag` in this
+ // `TagSystem`.
+ /* do nothing */
+ }
+ }
+ }
+
+ /**
+ * Returns a new `Tag` resolved from the provided I·R·I.
+ *
+ * ☡ This function throws if the I·R·I is not in the `.iriSpace`
+ * of the `TagSystem` associated with this constructor.
+ *
+ * ※ If the I·R·I is not recognized, this function returns
+ * `null`.
+ */
+ fromIRI(iri) {
+ const system = this.#system;
+ const storage = this.#storage;
+ const name = `${iri}`;
+ const prefix = `${system.iriSpace}`;
+ if (!name.startsWith(prefix)) {
+ // The I·R·I does not begin with the expected prefix.
+ throw new RangeError(
+ `I·R·I did not begin with the expected prefix: ${iri}`,
+ );
+ } else {
+ // The I·R·I begins with the expected prefix.
+ const identifier = name.substring(prefix.length);
+ try {
+ // Attempt to resolve the identifier.
+ const instance = storage.get(identifier);
+ return Tag.getSystem(instance) == system ? instance : null;
+ } catch {
+ // Do not throw for bad identifiers.
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Returns a new `Tag` resolved from the provided identifier.
+ *
+ * ☡ This function throws if the identifier is invalid.
+ *
+ * ※ If the identifier is valid but not recognized, this
+ * function returns `null`.
+ */
+ fromIdentifier(identifier) {
+ const system = this.#system;
+ const storage = this.#storage;
+ const instance = storage.get(identifier);
+ return Tag.getSystem(instance) == system ? instance : null;
+ }
+
+ /**
+ * Returns a new `Tag` resolved from the provided Tag U·R·I.
+ *
+ * ☡ This function throws if the provided Tag U·R·I does not
+ * match the tagging entity of this constructor’s `TagSystem`.
+ *
+ * ※ If the specific component of the Tag U·R·I is not
+ * recognized, this function returns `null`.
+ */
+ fromTagURI(tagURI) {
+ const system = this.#system;
+ const storage = this.#storage;
+ const tagName = `${tagURI}`;
+ const tagPrefix = `tag:${system.taggingEntity}:`;
+ if (!tagName.startsWith(tagPrefix)) {
+ // The Tag U·R·I does not begin with the expected prefix.
+ throw new RangeError(
+ `Tag U·R·I did not begin with the expected prefix: ${tagURI}`,
+ );
+ } else {
+ // The I·R·I begins with the expected prefix.
+ const identifier = tagName.substring(tagPrefix.length);
+ try {
+ // Attempt to resolve the identifier.
+ const instance = storage.get(identifier);
+ return Tag.getSystem(instance) == system ? instance : null;
+ } catch {
+ // Do not throw for bad identifiers.
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Yields the tag identifiers in the `TagSystem` associated with
+ * this constructor.
+ */
+ *identifiers() {
+ const system = this.#system;
+ const storage = this.#storage;
+ for (const [identifier, instance] of storage.entries()) {
+ // Iterate over the entries and yield the ones which are
+ // `Tag`s in this `TagSystem`.
+ if (Tag.getSystem(instance) == system) {
+ // The current instance is a `Tag` in this `TagSystem`.
+ yield identifier;
+ } else {
+ // The current instance is not a `Tag` in this `TagSystem`.
+ /* do nothing */
+ }
+ }
+ }
+
+ /**
+ * Returns a new `Tag` constructed from the provided data and
+ * with the provided identifier.
+ *
+ * ※ This function is not really intended for public usage.
+ */
+ [Storage.toInstance](data, identifier) {
+ const tag = new this(data.kind);
+ return Tag.assignData(tag, data, identifier);
+ }
+ },
+ TagSuper: tagConstructorBehaviours.TagSuper,
+ };
+})();
+