podns 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # PoDNS
2
+ This is a Node.JS implementation of [Pronouns over DNS](https://github.com/CutieZone/pronouns-over-dns), a way to define pronouns using DNS TXT records.
3
+
4
+ ## Usage
5
+ ```js
6
+ const getPronouns = require("podns");
7
+ await getPronouns("mauve.beer");
8
+ ```
9
+
10
+ ## Return values
11
+ ```js
12
+ {
13
+ type: "pronouns"|"wildcard"|"none"|"comment-only"
14
+ raw // raw data fetched
15
+ cleanedRaw // cleaned up version of the fetch data (e.g. "SHE/ Her #hello!" -> "she/her")
16
+ comment?
17
+
18
+ // everything below this requires type to be "pronouns" in order to exist
19
+ tags // array that can only contain "preferred" and/or "plural"
20
+ pronouns: {
21
+ subject //first part of the pronouns
22
+ object // second
23
+ possessiveDeterminer // third
24
+ possessivePronoun // fourth
25
+ reflexive // fifth
26
+ }
27
+ }
28
+ ```
package/dist/http.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function getPronounsRecords(domain: string): Promise<string[]>;
2
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO1E"}
package/dist/http.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPronounsRecords = getPronounsRecords;
4
+ const node_dns_1 = require("node:dns");
5
+ async function getPronounsRecords(domain) {
6
+ try {
7
+ const addresses = await node_dns_1.promises.resolveTxt("pronouns." + domain);
8
+ return addresses.flat();
9
+ }
10
+ catch (err) {
11
+ throw err;
12
+ }
13
+ }
@@ -0,0 +1,9 @@
1
+ import { PronounRecord } from "./types.js";
2
+ /**
3
+ * Get pronouns from a domain name and parse their values
4
+ * @param domain The domain to check. Must follow the format of `domain-name.tld`.
5
+ * @param silenceParseErrors Whether to silence errors and ignore misformed pronouns or not. Defaults to `true`.
6
+ * @returns The parsed pronouns
7
+ */
8
+ export default function getPronouns(domain: string, silenceParseErrors?: boolean): Promise<PronounRecord[]>;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,wBAA8B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,kBAAkB,UAAO,4BAYlF"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = getPronouns;
4
+ const http_js_1 = require("./http.js");
5
+ const parser_js_1 = require("./parser.js");
6
+ /**
7
+ * Get pronouns from a domain name and parse their values
8
+ * @param domain The domain to check. Must follow the format of `domain-name.tld`.
9
+ * @param silenceParseErrors Whether to silence errors and ignore misformed pronouns or not. Defaults to `true`.
10
+ * @returns The parsed pronouns
11
+ */
12
+ async function getPronouns(domain, silenceParseErrors = true) {
13
+ const pronounsRecords = await (0, http_js_1.getPronounsRecords)(domain);
14
+ let processed = [];
15
+ for (let r in pronounsRecords) {
16
+ const parsed = (0, parser_js_1.parseRecord)(r, silenceParseErrors);
17
+ if (parsed != null) {
18
+ processed.push(parsed);
19
+ }
20
+ }
21
+ return processed;
22
+ }
@@ -0,0 +1,9 @@
1
+ import { PronounRecord } from "./types.js";
2
+ /**
3
+ * Parses a record of pronouns
4
+ * @param record The record to parse
5
+ * @param silenceErrors Whether to silently error and return null or to `throw` errors
6
+ * @returns
7
+ */
8
+ export declare function parseRecord(record: string, silenceErrors?: boolean): PronounRecord | null;
9
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,aAAa,EAA8C,MAAM,YAAY,CAAC;AAEhI;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,UAAQ,GAAG,aAAa,GAAG,IAAI,CA8GvF"}
package/dist/parser.js ADDED
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRecord = parseRecord;
4
+ const types_js_1 = require("./types.js");
5
+ /**
6
+ * Parses a record of pronouns
7
+ * @param record The record to parse
8
+ * @param silenceErrors Whether to silently error and return null or to `throw` errors
9
+ * @returns
10
+ */
11
+ function parseRecord(record, silenceErrors = false) {
12
+ let parsed = record;
13
+ // separating the comment from the rest
14
+ let comment;
15
+ if (parsed.includes("#")) {
16
+ comment = record.split("#")[1];
17
+ parsed = record.split("#")[0];
18
+ }
19
+ // remove all whitespaces, make everything lowercase
20
+ parsed = parsed.trim().toLocaleLowerCase().replace(/\s/g, "");
21
+ // check special cases (wildcard, none, nothing other than a comment)
22
+ switch (parsed) {
23
+ case "*": {
24
+ return {
25
+ type: "wildcard",
26
+ comment: comment,
27
+ raw: record,
28
+ cleanedRaw: parsed
29
+ };
30
+ }
31
+ case "!": {
32
+ return {
33
+ type: "none",
34
+ comment: comment,
35
+ raw: record,
36
+ cleanedRaw: parsed
37
+ };
38
+ }
39
+ case "": {
40
+ if (comment) {
41
+ return {
42
+ type: "comment-only",
43
+ comment: comment,
44
+ raw: record,
45
+ cleanedRaw: parsed
46
+ };
47
+ }
48
+ else {
49
+ return null;
50
+ }
51
+ }
52
+ }
53
+ // check for tags
54
+ let tags = [];
55
+ if (parsed.includes(";")) {
56
+ const parsedTags = parsed.split(";").slice(1);
57
+ for (const i in parsedTags) {
58
+ let t = parsedTags[i];
59
+ if (t == "") {
60
+ continue;
61
+ }
62
+ if (!types_js_1.PRONOUN_TAGS.includes(t)) {
63
+ if (!silenceErrors) {
64
+ throw Error("fetched pronoun \"" + record + "\" contains invalid tag(s)");
65
+ }
66
+ else {
67
+ return null;
68
+ }
69
+ }
70
+ if (!tags.includes(t)) {
71
+ tags.push(t);
72
+ }
73
+ }
74
+ parsed = parsed.split(";")[0];
75
+ }
76
+ // check for pronoun parts
77
+ let parts = parsed.split("/");
78
+ if (parts.length < 2 || parts.length > 5) {
79
+ if (!silenceErrors) {
80
+ throw Error("fetched pronoun \"" + record + "\" is not formatted correctly");
81
+ }
82
+ else {
83
+ return null;
84
+ }
85
+ }
86
+ for (const i in parts) {
87
+ if (!/^[a-z]+$/.test(parts[i])) {
88
+ if (!silenceErrors) {
89
+ throw Error("fetched pronoun \"" + record + "\" contains invalid characters in its pronoun set");
90
+ }
91
+ else {
92
+ return null;
93
+ }
94
+ }
95
+ }
96
+ // special cases
97
+ if (parts[0] == "it" && parts[1] == "its") {
98
+ parts = ["it", "it", "its", "its", "itself"];
99
+ }
100
+ if (parts[0] == "they" && parts[1] == "them" && !tags.includes("plural")) {
101
+ tags.push("plural");
102
+ }
103
+ return {
104
+ pronouns: {
105
+ subject: parts[0],
106
+ object: parts[1],
107
+ possessiveDeterminer: parts[2],
108
+ possessivePronoun: parts[3],
109
+ reflexive: parts[4]
110
+ },
111
+ tags: tags,
112
+ type: "pronouns",
113
+ raw: record,
114
+ comment: comment,
115
+ cleanedRaw: parsed
116
+ };
117
+ }
@@ -0,0 +1,31 @@
1
+ interface PronounSet {
2
+ subject: string;
3
+ object: string;
4
+ possessiveDeterminer?: string;
5
+ possessivePronoun?: string;
6
+ reflexive?: string;
7
+ }
8
+ export declare const PRONOUN_TAGS: readonly ["preferred", "plural"];
9
+ export type PronounTag = typeof PRONOUN_TAGS[number];
10
+ interface Record {
11
+ raw: string;
12
+ cleanedRaw: string;
13
+ comment?: string;
14
+ }
15
+ export interface PronounsRecord extends Record {
16
+ type: 'pronouns';
17
+ pronouns: PronounSet;
18
+ tags: PronounTag[];
19
+ }
20
+ export interface WildcardRecord extends Record {
21
+ type: 'wildcard';
22
+ }
23
+ export interface NoneRecord extends Record {
24
+ type: 'none';
25
+ }
26
+ export interface CommentRecord extends Record {
27
+ type: 'comment-only';
28
+ }
29
+ export type PronounRecord = PronounsRecord | WildcardRecord | NoneRecord | CommentRecord;
30
+ export {};
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,UAAU,UAAU;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,kCAAmC,CAAC;AAC7D,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAErD,UAAU,MAAM;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAe,SAAQ,MAAM;IAC1C,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,UAAU,CAAC;IACrB,IAAI,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAe,SAAQ,MAAM;IAC1C,IAAI,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM;IACtC,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,MAAM;IACzC,IAAI,EAAE,cAAc,CAAA;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PRONOUN_TAGS = void 0;
4
+ exports.PRONOUN_TAGS = ['preferred', 'plural'];
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "podns",
3
+ "version": "1.0.0",
4
+ "author": "lumap <lumap@duck.com>",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "url": "https://codeberg.org/lumap/podns-js"
9
+ },
10
+ "devDependencies": {
11
+ "@types/node": "^25.0.9",
12
+ "typescript": "^5.9.3",
13
+ "vitest": "^4.0.17"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "prepublishOnly": "npm run build",
18
+ "test": "vitest run ./src/"
19
+ },
20
+ "license": "MIT"
21
+ }
@@ -0,0 +1,9 @@
1
+ import { test, assert } from "vitest";
2
+ import { getPronounsRecords } from "./http.js"
3
+
4
+ test("fetch pronouns from domain name & tld", async (t) => {
5
+ const pronouns = await getPronounsRecords("mauve.beer")
6
+ assert.equal(pronouns.includes("she/her;preferred"), true)
7
+ assert.equal(pronouns.includes("they/them"), true)
8
+ assert.equal(pronouns.length, 2)
9
+ })
package/src/http.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { promises as dnsPromises } from "node:dns";
2
+
3
+ export async function getPronounsRecords(domain: string): Promise<string[]> {
4
+ try {
5
+ const addresses = await dnsPromises.resolveTxt("pronouns." + domain);
6
+ return addresses.flat();
7
+ } catch (err) {
8
+ throw err;
9
+ }
10
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { getPronounsRecords} from "./http.js";
2
+ import { parseRecord } from "./parser.js";
3
+ import { PronounRecord } from "./types.js";
4
+
5
+ /**
6
+ * Get pronouns from a domain name and parse their values
7
+ * @param domain The domain to check. Must follow the format of `domain-name.tld`.
8
+ * @param silenceParseErrors Whether to silence errors and ignore misformed pronouns or not. Defaults to `true`.
9
+ * @returns The parsed pronouns
10
+ */
11
+ export default async function getPronouns(domain: string, silenceParseErrors = true) {
12
+ const pronounsRecords = await getPronounsRecords(domain);
13
+
14
+ let processed: PronounRecord[] = []
15
+ for (let r in pronounsRecords) {
16
+ const parsed = parseRecord(r, silenceParseErrors)
17
+ if (parsed != null) {
18
+ processed.push(parsed)
19
+ }
20
+ }
21
+
22
+ return processed
23
+ }
@@ -0,0 +1,137 @@
1
+ import { parseRecord } from "./parser.js";
2
+ import {test, assert, expect} from "vitest";
3
+ import { CommentRecord, NoneRecord, PronounsRecord, WildcardRecord } from "./types.js";
4
+
5
+ // special cases
6
+ test("parse nothing", () => {
7
+ assert.equal(parseRecord(""), null)
8
+ })
9
+
10
+ test("parse comment-only", () => {
11
+ const parsed = parseRecord("#meow") as CommentRecord;
12
+ assert.notEqual(parsed, null)
13
+ assert.equal(parsed.type, "comment-only")
14
+ assert.equal(parsed.comment, "meow")
15
+ assert.equal(parsed.raw, "#meow")
16
+ assert.equal(parsed.cleanedRaw, "")
17
+ })
18
+
19
+ test("parse none", () => {
20
+ const parsed = parseRecord("! ") as NoneRecord;
21
+ assert.notEqual(parsed, null)
22
+ assert.equal(parsed.type, "none")
23
+ assert.equal(parsed.comment, undefined)
24
+ assert.equal(parsed.raw, "! ")
25
+ assert.equal(parsed.cleanedRaw, "!")
26
+ })
27
+
28
+ test("parse wildcard", () => {
29
+ const parsed = parseRecord(" *") as WildcardRecord;
30
+ assert.notEqual(parsed, null)
31
+ assert.equal(parsed.type, "wildcard")
32
+ assert.equal(parsed.comment, undefined)
33
+ assert.equal(parsed.raw, " *")
34
+ assert.equal(parsed.cleanedRaw, "*")
35
+ })
36
+
37
+ // invalid cases
38
+ test("parse wildcard and none", () => {
39
+ expect(()=>parseRecord("*!")).toThrowError("fetched pronoun \"*!\" is not formatted correctly")
40
+ })
41
+
42
+ test("parse invalid tag", () => {
43
+ expect(() => parseRecord("she;meow")).toThrowError("fetched pronoun \"she;meow\" contains invalid tag(s)")
44
+ })
45
+
46
+ test("parse invalid pronoun set", () => {
47
+ expect(() => parseRecord("she/Ìê®;")).toThrowError("fetched pronoun \"she/Ìê®;\" contains invalid characters in its pronoun set")
48
+ })
49
+ test("parse invalid pronoun set 2", () => {
50
+ expect(() => parseRecord("she/")).toThrowError("fetched pronoun \"she/\" contains invalid characters in its pronoun set")
51
+ })
52
+
53
+ test("parse return null if errors are silenced", () => {
54
+ assert.equal(parseRecord("meow3", true), null)
55
+ })
56
+
57
+ // valid cases
58
+ test("parse pronouns without tags or comments",() => {
59
+ const parsed = parseRecord("she/her") as PronounsRecord;
60
+ assert.notEqual(parsed, null)
61
+ assert.equal(parsed.type, "pronouns")
62
+ assert.equal(parsed.comment, undefined)
63
+ assert.equal(Array.isArray(parsed.tags), true)
64
+ assert.equal(parsed.tags[0], undefined)
65
+ assert.equal(parsed.raw, "she/her")
66
+ assert.equal(parsed.pronouns.subject, "she")
67
+ assert.equal(parsed.pronouns.object, "her")
68
+ assert.equal(parsed.cleanedRaw, "she/her")
69
+ assert.equal(parsed.pronouns.possessiveDeterminer, undefined)
70
+ assert.equal(parsed.pronouns.possessivePronoun, undefined)
71
+ assert.equal(parsed.pronouns.reflexive, undefined)
72
+ })
73
+
74
+ test("parse pronouns with a tag and a comment", () => {
75
+ const parsed = parseRecord("she/HUr/hur/HUR/hur; preferred #O Cholera Czy To Freddy Fazbear") as PronounsRecord;
76
+ assert.notEqual(parsed, null)
77
+ assert.equal(parsed.type, "pronouns")
78
+ assert.equal(parsed.comment, "O Cholera Czy To Freddy Fazbear")
79
+ assert.equal(Array.isArray(parsed.tags), true)
80
+ assert.equal(parsed.tags[0], "preferred")
81
+ assert.equal(parsed.raw, "she/HUr/hur/HUR/hur; preferred #O Cholera Czy To Freddy Fazbear")
82
+ assert.equal(parsed.cleanedRaw, "she/hur/hur/hur/hur")
83
+ assert.equal(parsed.pronouns.subject, "she")
84
+ assert.equal(parsed.pronouns.object, "hur")
85
+ assert.equal(parsed.pronouns.possessiveDeterminer, "hur")
86
+ assert.equal(parsed.pronouns.possessivePronoun, "hur")
87
+ assert.equal(parsed.pronouns.reflexive, "hur")
88
+ })
89
+
90
+ test("parse pronouns with multiple tags and a comment", () => {
91
+ const parsed = parseRecord("she/ HUr/hur/HUR/hur; preferred;;;plural #O Cholera Czy To Freddy Fazbear") as PronounsRecord;
92
+ assert.notEqual(parsed, null)
93
+ assert.equal(parsed.type, "pronouns")
94
+ assert.equal(parsed.comment, "O Cholera Czy To Freddy Fazbear")
95
+ assert.equal(Array.isArray(parsed.tags), true)
96
+ assert.equal(parsed.tags[0], "preferred")
97
+ assert.equal(parsed.tags[1], "plural")
98
+ assert.equal(parsed.raw, "she/ HUr/hur/HUR/hur; preferred;;;plural #O Cholera Czy To Freddy Fazbear")
99
+ assert.equal(parsed.cleanedRaw, "she/hur/hur/hur/hur")
100
+ assert.equal(parsed.pronouns.subject, "she")
101
+ assert.equal(parsed.pronouns.object, "hur")
102
+ assert.equal(parsed.pronouns.possessiveDeterminer, "hur")
103
+ assert.equal(parsed.pronouns.possessivePronoun, "hur")
104
+ assert.equal(parsed.pronouns.reflexive, "hur")
105
+ })
106
+
107
+ test("parse it/its", () => {
108
+ const parsed = parseRecord("it/its # meow!") as PronounsRecord;
109
+ assert.notEqual(parsed, null)
110
+ assert.equal(parsed.type, "pronouns")
111
+ assert.equal(parsed.comment, " meow!")
112
+ assert.equal(Array.isArray(parsed.tags), true)
113
+ assert.equal(parsed.tags[0], undefined)
114
+ assert.equal(parsed.raw, "it/its # meow!")
115
+ assert.equal(parsed.cleanedRaw, "it/its")
116
+ assert.equal(parsed.pronouns.subject, "it")
117
+ assert.equal(parsed.pronouns.object, "it")
118
+ assert.equal(parsed.pronouns.possessiveDeterminer, "its")
119
+ assert.equal(parsed.pronouns.possessivePronoun, "its")
120
+ assert.equal(parsed.pronouns.reflexive, "itself")
121
+ })
122
+
123
+ test("parse they/them is automatically plural", () => {
124
+ const parsed = parseRecord("they/them") as PronounsRecord;
125
+ assert.notEqual(parsed, null)
126
+ assert.equal(parsed.type, "pronouns")
127
+ assert.equal(parsed.comment, undefined)
128
+ assert.equal(Array.isArray(parsed.tags), true)
129
+ assert.equal(parsed.tags[0], "plural")
130
+ assert.equal(parsed.raw, "they/them")
131
+ assert.equal(parsed.cleanedRaw, "they/them")
132
+ assert.equal(parsed.pronouns.subject, "they")
133
+ assert.equal(parsed.pronouns.object, "them")
134
+ assert.equal(parsed.pronouns.possessiveDeterminer, undefined)
135
+ assert.equal(parsed.pronouns.possessivePronoun, undefined)
136
+ assert.equal(parsed.pronouns.reflexive, undefined)
137
+ })
package/src/parser.ts ADDED
@@ -0,0 +1,119 @@
1
+ import { CommentRecord, NoneRecord, PRONOUN_TAGS, PronounRecord, PronounsRecord, PronounTag, WildcardRecord } from "./types.js";
2
+
3
+ /**
4
+ * Parses a record of pronouns
5
+ * @param record The record to parse
6
+ * @param silenceErrors Whether to silently error and return null or to `throw` errors
7
+ * @returns
8
+ */
9
+ export function parseRecord(record: string, silenceErrors = false): PronounRecord | null {
10
+ let parsed = record;
11
+ // separating the comment from the rest
12
+ let comment;
13
+ if (parsed.includes("#")) {
14
+ comment = record.split("#")[1]
15
+ parsed = record.split("#")[0]
16
+ }
17
+
18
+ // remove all whitespaces, make everything lowercase
19
+ parsed = parsed.trim().toLocaleLowerCase().replace(/\s/g, "")
20
+
21
+ // check special cases (wildcard, none, nothing other than a comment)
22
+ switch (parsed) {
23
+ case "*": {
24
+ return {
25
+ type: "wildcard",
26
+ comment: comment,
27
+ raw: record,
28
+ cleanedRaw: parsed
29
+ } as WildcardRecord
30
+ }
31
+ case "!": {
32
+ return {
33
+ type: "none",
34
+ comment: comment,
35
+ raw: record,
36
+ cleanedRaw: parsed
37
+ } as NoneRecord
38
+ }
39
+ case "": {
40
+ if (comment) {
41
+ return {
42
+ type: "comment-only",
43
+ comment: comment,
44
+ raw: record,
45
+ cleanedRaw: parsed
46
+ } as CommentRecord
47
+ } else {
48
+ return null;
49
+ }
50
+ }
51
+ }
52
+
53
+ // check for tags
54
+ let tags: PronounTag[] = [];
55
+ if (parsed.includes(";")) {
56
+ const parsedTags = parsed.split(";").slice(1);
57
+ for (const i in parsedTags) {
58
+ let t = parsedTags[i]
59
+ if (t == "") {
60
+ continue
61
+ }
62
+ if (!PRONOUN_TAGS.includes(t as PronounTag)) {
63
+ if (!silenceErrors) {
64
+ throw Error("fetched pronoun \""+record+"\" contains invalid tag(s)")
65
+ } else {
66
+ return null
67
+ }
68
+ }
69
+ if (!tags.includes(t as PronounTag)) {
70
+ tags.push(t as PronounTag)
71
+ }
72
+ }
73
+ parsed = parsed.split(";")[0]
74
+ }
75
+
76
+ // check for pronoun parts
77
+ let parts = parsed.split("/")
78
+ if (parts.length < 2 || parts.length > 5) {
79
+ if (!silenceErrors) {
80
+ throw Error("fetched pronoun \"" + record + "\" is not formatted correctly")
81
+ } else {
82
+ return null
83
+ }
84
+ }
85
+
86
+ for (const i in parts) {
87
+ if (!/^[a-z]+$/.test(parts[i])) {
88
+ if (!silenceErrors) {
89
+ throw Error("fetched pronoun \"" + record +"\" contains invalid characters in its pronoun set")
90
+ } else {
91
+ return null
92
+ }
93
+ }
94
+ }
95
+
96
+ // special cases
97
+ if (parts[0] == "it" && parts[1] == "its") {
98
+ parts = ["it","it","its","its","itself"]
99
+ }
100
+ if (parts[0] == "they" && parts[1] == "them" && !tags.includes("plural")) {
101
+ tags.push("plural")
102
+ }
103
+
104
+ return {
105
+ pronouns: {
106
+ subject: parts[0],
107
+ object: parts[1],
108
+ possessiveDeterminer: parts[2],
109
+ possessivePronoun: parts[3],
110
+ reflexive: parts[4]
111
+
112
+ },
113
+ tags: tags,
114
+ type: "pronouns",
115
+ raw: record,
116
+ comment: comment,
117
+ cleanedRaw: parsed
118
+ } as PronounsRecord
119
+ }
package/src/types.ts ADDED
@@ -0,0 +1,36 @@
1
+ interface PronounSet {
2
+ subject: string;
3
+ object: string;
4
+ possessiveDeterminer?: string;
5
+ possessivePronoun?: string;
6
+ reflexive?: string;
7
+ }
8
+
9
+ export const PRONOUN_TAGS = ['preferred', 'plural'] as const;
10
+ export type PronounTag = typeof PRONOUN_TAGS[number];
11
+
12
+ interface Record {
13
+ raw: string;
14
+ cleanedRaw: string;
15
+ comment?: string;
16
+ }
17
+
18
+ export interface PronounsRecord extends Record {
19
+ type: 'pronouns';
20
+ pronouns: PronounSet;
21
+ tags: PronounTag[];
22
+ }
23
+
24
+ export interface WildcardRecord extends Record {
25
+ type: 'wildcard';
26
+ }
27
+
28
+ export interface NoneRecord extends Record {
29
+ type: 'none';
30
+ }
31
+
32
+ export interface CommentRecord extends Record {
33
+ type: 'comment-only'
34
+ }
35
+
36
+ export type PronounRecord = PronounsRecord | WildcardRecord | NoneRecord | CommentRecord;
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "nodenext",
5
+ "declaration": true,
6
+ "declarationMap": true,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ },
14
+ "include": [
15
+ "src/**/*"
16
+ ],
17
+ "exclude": [
18
+ "node_modules",
19
+ "dist",
20
+ "src/**/*.test.ts"
21
+ ]
22
+ }