pika-id
Version:
The pragmatic ID system
119 lines (118 loc) • 5.67 kB
JavaScript
;
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Pika_snowflake, _Pika_suppressPrefixWarnings, _Pika_nodeId;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pika = exports.DEFAULT_EPOCH = exports.VALID_PREFIX = void 0;
const crypto_1 = require("crypto");
const os_1 = require("os");
const logger_1 = require("./logger");
const snowflake_1 = require("./snowflake");
exports.VALID_PREFIX = /^[a-z0-9_]+$/i;
exports.DEFAULT_EPOCH = 1640995200000n; // Jan 1 2022
class Pika {
/**
* @param prefixes a list of PikaPrefixRecords to initialize pika with
* @param opts misc. options to initialize pika with
*/
constructor(prefixes, { nodeId, ...opts } = {}) {
this.prefixes = {};
_Pika_snowflake.set(this, void 0);
_Pika_suppressPrefixWarnings.set(this, void 0);
/**
* The generated or passed in node ID for this Pika instance
* @internal
*/
_Pika_nodeId.set(this, void 0);
__classPrivateFieldSet(this, _Pika_nodeId, nodeId ? BigInt(nodeId) % 1024n : this.computeNodeId(), "f");
__classPrivateFieldSet(this, _Pika_snowflake, new snowflake_1.Snowflake(opts.epoch || exports.DEFAULT_EPOCH, __classPrivateFieldGet(this, _Pika_nodeId, "f")), "f");
__classPrivateFieldSet(this, _Pika_suppressPrefixWarnings, opts.suppressPrefixWarnings ?? false, "f");
this.prefixes = prefixes.reduce((prefixes, definition) => {
if (typeof definition === "string") {
return {
...prefixes,
[definition]: { prefix: definition },
};
}
return {
...prefixes,
[definition.prefix]: definition,
};
}, {});
}
gen(prefix) {
if (!exports.VALID_PREFIX.test(prefix)) {
throw TypeError(`invalid prefix; prefixes must be alphanumeric (a-z0-9_) and may include underscores; received: ${prefix}`);
}
if (!this.prefixes[prefix] && !__classPrivateFieldGet(this, _Pika_suppressPrefixWarnings, "f")) {
(0, logger_1.warn)(`Unregistered prefix (${prefix}) was used. This can cause unknown behavior - see https://github.com/hopinc/pika/tree/main/impl/js for details.`);
}
const snowflake = __classPrivateFieldGet(this, _Pika_snowflake, "f").gen();
return `${prefix.toLowerCase()}_${Buffer.from((this.prefixes[prefix]?.secure
? `s_${(0, crypto_1.randomBytes)(16).toString("hex")}_`
: "") + snowflake).toString("base64url")}`;
}
/**
* Gen a Snowflake, if you really need one
*/
genSnowflake() {
return __classPrivateFieldGet(this, _Pika_snowflake, "f").gen();
}
decode(id) {
try {
const s = id.split("_");
const tail = s[s.length - 1];
const prefix = s.slice(0, s.length - 1).join("_");
const decodedTail = Buffer.from(tail, "base64").toString();
const sf = decodedTail.split("_").pop();
if (!sf) {
throw Error("attempted to decode invalid pika; tail was corrupt");
}
const { id: snowflake, ...v } = __classPrivateFieldGet(this, _Pika_snowflake, "f").deconstruct(sf);
return {
prefix,
tail,
prefix_record: this.prefixes[prefix],
prefixRecord: this.prefixes[prefix],
snowflake,
version: 1,
...v,
};
}
catch (e) {
(0, logger_1.error)("Failed to decode ID", id);
throw e;
}
}
/**
* Derives this machine's node ID from the MAC address of the first
* public network interface it finds
* @returns The computed node ID (0-1023)
*/
computeNodeId() {
try {
const interfaces = Object.values((0, os_1.networkInterfaces)());
const firstValidInterface = interfaces.filter((iface) => iface && iface[0].mac !== "00:00:00:00:00:00")[0];
if (!firstValidInterface) {
throw new Error("no valid mac address found");
}
const mac = firstValidInterface[0].mac;
return BigInt(parseInt(mac.split(":").join(""), 16) % 1024);
}
catch (e) {
(0, logger_1.warn)("Failed to compute node ID, falling back to 0. Error:\n", e);
return 0n;
}
}
}
exports.Pika = Pika;
_Pika_snowflake = new WeakMap(), _Pika_suppressPrefixWarnings = new WeakMap(), _Pika_nodeId = new WeakMap();