protocol-tag-utils
A tiny, zero-dependency, set of utilities for interacting with Data-Protocol
tags. Browser, Node, Bun, and Deno compatible.
Why
It is fundamental to note that, in ANS-104, tags are ordered.
ANS-115 specifies how to utilize Data-Protocols to compose application level
specification using ANS-104 tags.
However, ambiguity arises when a piece of data implements many Data-Protocol
which specify the same tag name:
const tags = [
{ name: "Data-Protocol", value: "ao" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Process" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Variant", value: "0.0.2" },
];
This ambuguity can lead to unexpected behavior in workarounds in subsequent
implementations.
Solution
By enforcing one additional simple convention, this ambiguity is massively
curtailed:
Tags are "associated" with the most recent Data-Protocol tag. **A
corollary is that "unassociated" tags, not belonging to a Data-Protocol,
appear at the beginning, before the first Data-Protocol tag.
const tags = [
{ name: "Unassociated", value: "Tag" },
{ name: "Another", value: "Unassociated" },
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
];
This module provides utilties for interacting with tags, using the above
convention.
Usage
import {
concat,
concatUnassoc,
create,
findAll,
findAllByName,
findByName,
parse,
parseAll,
parseAllUnassoc,
parseUnassoc,
proto,
removeAll,
removeAllByName,
update,
} from "@permaweb/protocol-tag-utils";
const tags = [
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
];
const aoType = findByName("ao", "Type", tags);
const ao = proto("ao");
const zone = proto("zone");
const aoType = ao.value("Type", tags);
const zoneTypes = zone.values("Type", tags);
Passing protocol first, over and over, might get verbose. Alternatively, you
can use the proto helper.
findAll
Extract the tags associated with the provided Data-Protocol.
If the Data-Protocol tag is NOT found, then an empty array is returned
import { findAll } from "@permaweb/protocol-tag-utils";
const tags = [];
const aoTags = findAll("ao", tags);
findAllByName
Extract the tags, with the name, associated with the provided Data-Protocol.
import { findAllByName } from '@permaweb/protocol-tag-utils'
const tags = []
const zoneTypes = findAllByName('zone', 'Type' tags)
findByName
Extract the FIRST tag, with the name, associated with the provided
Data-Protocol.
import { findByName } from '@permaweb/protocol-tag-utils'
const tags = []
const aoType = findAllByName('ao', 'Type' tags)
create
Associate an array of tags associated with the Data-Protocol. The
Data-Protocol tag will be prepended to the front of the array.
import { create } from "@permaweb/protocol-tag-utils";
const pTags = [{ name: "Foo", value: "Bar" }];
const aoTags = create("ao", pTags);
update
Replace the tags, associated with the Data-Protocol, with the provided tags.
If there are no associated tags for the Data-Protocol, then the new section is
concatenated to the end of all tags.
NO deduplication is performed on the associated tags.
import { update } from "@permaweb/protocol-tag-utils";
const tags = [
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
];
const pTags = [{ name: "Foo", value: "Bar" }, { name: "Cool", value: "Beans" }];
update("ao", pTags, tags);
concat
Same update, except do not replace the associated tags, and instead
concatenate to them.
import { concat } from "@permaweb/protocol-tag-utils";
const tags = [
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
];
const pTags = [{ name: "Foo", value: "Bar" }, { name: "Cool", value: "Beans" }];
concat("ao", pTags, tags);
removeAll
Remove the Data-Protocol section and all associated tags
import { removeAll } from "@permaweb/protocol-tag-utils";
const tags = [
{ name: "Unassociated", value: "Tag" },
{ name: "Another", value: "Unassociated" },
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
];
removeAll("ao", tags);
removeAllByName
Remove all tags, with the name, associated with the Data-Protocol.
import { removeAllByName } from "@permaweb/protocol-tag-utils";
const tags = [
{ name: "Unassociated", value: "Tag" },
{ name: "Another", value: "Unassociated" },
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Type", value: "Other" },
{ name: "Variant", value: "0.0.2" },
];
removeAllByName("zone", "Type", tags);
parse
Parse tags, associated with the Data-Protocol, into an object with key-value
pairs of name -> value.
If multiple tags are found, then the FIRST tag value is used, and subsequent
values are discarded. If you'd like to preserve all values, then use
parseAll
import { parse } from "@permaweb/protocol-tag-utils";
const tags = [];
const aoParsed = parse("ao", tags);
parseAll
Parse tags, associated with the Data-Protocol, into an object with key-value
pairs of name -> an array of values.
At each key, the values in each array will be in order of appearance
import { parseAll } from "@permaweb/protocol-tag-utils";
const tags = [];
const aoParsed = parseAll("ao", tags);
proto
Instead of constantly passing protocol as the first argument every time, you
can use this helper.
Build a @permaweb/protocol-tag-utils API for a single Data-Protocol
import { proto } from "@permaweb/protocol-tag-utils";
const ao = proto("ao");
const zone = proto("zone");
const tags = [
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
];
const aoType = ao.value("Type", tags);
const zoneTypes = zone.values("Type", tags);
Unassociated Tags
The module also has utilities for interacting with tags not associated with any
Data-Protocol ie. tags before any Data-Protocol tag. These tags are referred
to as "unassociated" tags.
If you'd like to simply add an uassociated tag to the beginning, simply use
unshift.
concatUnassoc
Add unassociated tags to the end of unassociated section.
import { concatUnassoc } from "@permaweb/protocol-tag-utils"
const tags = [
{ name: 'Random', value: 'Tag' }
{ name: "Data-Protocol", value: "ao" },
{ name: "Type", value: "Process" },
{ name: "Variant", value: "ao.TN.1" },
{ name: "Data-Protocol", value: "zone" },
{ name: "Type", value: "Profile" },
{ name: "Variant", value: "0.0.2" },
]
concatUnassoc([{ name: 'Another', value: 'One' }], tags)
parseUnassoc
Same as parse, but for unassociated tags
import { parseUnassoc } from "@permaweb/protocol-tag-utils";
const tags = [];
parseUnassoc(tags);
parseAllUassoc
Same as parseAll, but for unassociated tags
import { parseAllUnassoc } from "@permaweb/protocol-tag-utils";
const tags = [];
parseAllUnassoc(tags);