UNPKG

pika-id

Version:

The pragmatic ID system

119 lines (118 loc) 5.67 kB
"use strict"; 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();