add support for v2 hashing algorithm.

update README.md and demo file to be more in sync.
update to v0.10.0.
This commit is contained in:
2026-03-29 22:34:43 -04:00
parent d5363c3960
commit c776b5df6a
33 changed files with 2255 additions and 1524 deletions

View File

@@ -1 +1 @@
export declare const __dirname: string;
export declare const HASH_V2_EPOCH = "2026-07-01T00:00:00Z";

5
dist-node/constants.js Normal file
View File

@@ -0,0 +1,5 @@
const HASH_V2_EPOCH = `2026-07-01T00:00:00Z`;
export {
HASH_V2_EPOCH
};
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sources":["../src/constants.ts"],"sourcesContent":["export const HASH_V2_EPOCH = `2026-07-01T00:00:00Z`;\n"],"names":[],"mappings":"AAAO,MAAM,gBAAgB;"}

View File

@@ -1,2 +1,3 @@
import { Twt } from './types.ts';
export default function hashTwt(twt: Twt): string;
export type HashableTwt = Pick<Twt, "content" | "created" | "url"> & Partial<Omit<Twt, "content" | "created" | "url">>;
export default function hashTwt(twt: HashableTwt, version?: number): string;

View File

@@ -1,5 +1,6 @@
import { blake2b } from "@exodus/blakejs";
import { base32Encode } from "./utils.js";
import { HASH_V2_EPOCH } from "./constants.js";
const dateRegex = /^(\d{4})-(\d{2})-(\d{2})([tT ])(\d{2}):(\d{2}):(\d{2})\.?(\d{3})?(?:(?:([+-]\d{2}):?(\d{2}))|Z)?$/;
const formatRFC3339 = (date) => {
const pad = (num = 0) => `${+num < 10 ? 0 : ""}${+num}`;
@@ -28,10 +29,14 @@ const formatRFC3339 = (date) => {
offset
].join("");
};
function hashTwt(twt) {
function hashTwt(twt, version = 0) {
const created = formatRFC3339(twt.created);
const payload = [twt.url, created, twt.content].join("\n");
return base32Encode(blake2b(payload, void 0, 32)).toLowerCase().slice(-7);
const encoded = base32Encode(blake2b(payload, void 0, 32)).toLowerCase();
const createdDate = new Date(twt.created), epochDate = new Date(HASH_V2_EPOCH);
if (version === 1 || !version && createdDate < epochDate)
return encoded.slice(-7);
return encoded.slice(0, 12);
}
export {
hashTwt as default

View File

@@ -1 +1 @@
{"version":3,"file":"hashTwt.js","sources":["../src/hashTwt.ts"],"sourcesContent":["import type { Twt } from \"./types.ts\";\n\nimport { blake2b } from \"@exodus/blakejs\";\n\nimport { base32Encode } from \"./utils.ts\";\n\nconst dateRegex =\n\t/^(\\d{4})-(\\d{2})-(\\d{2})([tT ])(\\d{2}):(\\d{2}):(\\d{2})\\.?(\\d{3})?(?:(?:([+-]\\d{2}):?(\\d{2}))|Z)?$/;\n\nconst formatRFC3339 = (date: string) => {\n\tconst pad = (num: number | string = 0) => `${+num < 10 ? 0 : \"\"}${+num}`;\n\tconst padYear = (num: number | string = 0) =>\n\t\t`${+num < 1000 ? 0 : \"\"}${+num < 100 ? 0 : \"\"}${\n\t\t\t+num < 10 ? 0 : \"\"\n\t\t}${+num}`;\n\n\tlet m = dateRegex.exec(date);\n\n\t//if timezone is undefined, it must be Z or nothing (otherwise the group would have captured).\n\tif (m && m?.[9] === undefined) {\n\t\t//Use UTC.\n\t\tm[9] = \"+00\";\n\t}\n\tif (m && m?.[10] === undefined) {\n\t\tm[10] = \"00\";\n\t}\n\n\tconst offset = `${m?.[9]}:${m?.[10]}`.replace(/[+-]?00:00$/, \"Z\");\n\n\treturn [\n\t\tpadYear(m?.[1]),\n\t\t\"-\",\n\t\tpad(m?.[2]),\n\t\t\"-\",\n\t\tpad(m?.[3]),\n\t\tm?.[4],\n\t\tpad(m?.[5]),\n\t\t\":\",\n\t\tpad(m?.[6]),\n\t\t\":\",\n\t\tpad(m?.[7]),\n\t\t//ignore milliseconds (m[8])\n\t\toffset,\n\t].join(\"\");\n};\n\nexport default function hashTwt(twt: Twt): string {\n\tconst created = formatRFC3339(twt.created);\n\tconst payload = [twt.url, created, twt.content].join(\"\\n\");\n\n\treturn base32Encode(blake2b(payload, undefined, 32))\n\t\t.toLowerCase()\n\t\t.slice(-7);\n}\n"],"names":[],"mappings":";;AAMA,MAAM,YACL;AAED,MAAM,gBAAgB,CAAC,SAAiB;AACvC,QAAM,MAAM,CAAC,MAAuB,MAAM,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,GAAG,CAAC,GAAG;AACtE,QAAM,UAAU,CAAC,MAAuB,MACvC,GAAG,CAAC,MAAM,MAAO,IAAI,EAAE,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,GAC5C,CAAC,MAAM,KAAK,IAAI,EACjB,GAAG,CAAC,GAAG;AAER,MAAI,IAAI,UAAU,KAAK,IAAI;AAG3B,MAAI,KAAK,IAAI,CAAC,MAAM,QAAW;AAE9B,MAAE,CAAC,IAAI;AAAA,EACR;AACA,MAAI,KAAK,IAAI,EAAE,MAAM,QAAW;AAC/B,MAAE,EAAE,IAAI;AAAA,EACT;AAEA,QAAM,SAAS,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,QAAQ,eAAe,GAAG;AAEhE,SAAO;AAAA,IACN,QAAQ,IAAI,CAAC,CAAC;AAAA,IACd;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA,IACV;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA,IACV,IAAI,CAAC;AAAA,IACL,IAAI,IAAI,CAAC,CAAC;AAAA,IACV;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA,IACV;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA;AAAA,IAEV;AAAA,EAAA,EACC,KAAK,EAAE;AACV;AAEA,SAAwB,QAAQ,KAAkB;AACjD,QAAM,UAAU,cAAc,IAAI,OAAO;AACzC,QAAM,UAAU,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,EAAE,KAAK,IAAI;AAEzD,SAAO,aAAa,QAAQ,SAAS,QAAW,EAAE,CAAC,EACjD,YAAA,EACA,MAAM,EAAE;AACX;"}
{"version":3,"file":"hashTwt.js","sources":["../src/hashTwt.ts"],"sourcesContent":["import type { Twt } from \"./types.ts\";\n\nimport { blake2b } from \"@exodus/blakejs\";\n\nimport { base32Encode } from \"./utils.ts\";\nimport { HASH_V2_EPOCH } from \"./constants.ts\";\n\nconst dateRegex =\n\t/^(\\d{4})-(\\d{2})-(\\d{2})([tT ])(\\d{2}):(\\d{2}):(\\d{2})\\.?(\\d{3})?(?:(?:([+-]\\d{2}):?(\\d{2}))|Z)?$/;\n\nconst formatRFC3339 = (date: string) => {\n\tconst pad = (num: number | string = 0) => `${+num < 10 ? 0 : \"\"}${+num}`;\n\tconst padYear = (num: number | string = 0) =>\n\t\t`${+num < 1000 ? 0 : \"\"}${+num < 100 ? 0 : \"\"}${\n\t\t\t+num < 10 ? 0 : \"\"\n\t\t}${+num}`;\n\n\tlet m = dateRegex.exec(date);\n\n\t//if timezone is undefined, it must be Z or nothing (otherwise the group would have captured).\n\tif (m && m?.[9] === undefined) {\n\t\t//Use UTC.\n\t\tm[9] = \"+00\";\n\t}\n\tif (m && m?.[10] === undefined) {\n\t\tm[10] = \"00\";\n\t}\n\n\tconst offset = `${m?.[9]}:${m?.[10]}`.replace(/[+-]?00:00$/, \"Z\");\n\n\treturn [\n\t\tpadYear(m?.[1]),\n\t\t\"-\",\n\t\tpad(m?.[2]),\n\t\t\"-\",\n\t\tpad(m?.[3]),\n\t\tm?.[4],\n\t\tpad(m?.[5]),\n\t\t\":\",\n\t\tpad(m?.[6]),\n\t\t\":\",\n\t\tpad(m?.[7]),\n\t\t//ignore milliseconds (m[8])\n\t\toffset,\n\t].join(\"\");\n};\n\nexport type HashableTwt = Pick<Twt, \"content\" | \"created\" | \"url\"> &\n\tPartial<Omit<Twt, \"content\" | \"created\" | \"url\">>;\n\nexport default function hashTwt(twt: HashableTwt, version = 0): string {\n\tconst created = formatRFC3339(twt.created);\n\tconst payload = [twt.url, created, twt.content].join(\"\\n\");\n\n\tconst encoded = base32Encode(blake2b(payload, undefined, 32)).toLowerCase();\n\n\tconst createdDate = new Date(twt.created),\n\t\tepochDate = new Date(HASH_V2_EPOCH);\n\n\tif (version === 1 || (!version && createdDate < epochDate))\n\t\treturn encoded.slice(-7);\n\treturn encoded.slice(0, 12);\n}\n"],"names":[],"mappings":";;;AAOA,MAAM,YACL;AAED,MAAM,gBAAgB,CAAC,SAAiB;AACvC,QAAM,MAAM,CAAC,MAAuB,MAAM,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,GAAG,CAAC,GAAG;AACtE,QAAM,UAAU,CAAC,MAAuB,MACvC,GAAG,CAAC,MAAM,MAAO,IAAI,EAAE,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,GAC5C,CAAC,MAAM,KAAK,IAAI,EACjB,GAAG,CAAC,GAAG;AAER,MAAI,IAAI,UAAU,KAAK,IAAI;AAG3B,MAAI,KAAK,IAAI,CAAC,MAAM,QAAW;AAE9B,MAAE,CAAC,IAAI;AAAA,EACR;AACA,MAAI,KAAK,IAAI,EAAE,MAAM,QAAW;AAC/B,MAAE,EAAE,IAAI;AAAA,EACT;AAEA,QAAM,SAAS,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,QAAQ,eAAe,GAAG;AAEhE,SAAO;AAAA,IACN,QAAQ,IAAI,CAAC,CAAC;AAAA,IACd;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA,IACV;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA,IACV,IAAI,CAAC;AAAA,IACL,IAAI,IAAI,CAAC,CAAC;AAAA,IACV;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA,IACV;AAAA,IACA,IAAI,IAAI,CAAC,CAAC;AAAA;AAAA,IAEV;AAAA,EAAA,EACC,KAAK,EAAE;AACV;AAKA,SAAwB,QAAQ,KAAkB,UAAU,GAAW;AACtE,QAAM,UAAU,cAAc,IAAI,OAAO;AACzC,QAAM,UAAU,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,EAAE,KAAK,IAAI;AAEzD,QAAM,UAAU,aAAa,QAAQ,SAAS,QAAW,EAAE,CAAC,EAAE,YAAA;AAE9D,QAAM,cAAc,IAAI,KAAK,IAAI,OAAO,GACvC,YAAY,IAAI,KAAK,aAAa;AAEnC,MAAI,YAAY,KAAM,CAAC,WAAW,cAAc;AAC/C,WAAO,QAAQ,MAAM,EAAE;AACxB,SAAO,QAAQ,MAAM,GAAG,EAAE;AAC3B;"}