UNPKG

@wandelbots/nova-js

Version:

Official JS client for the Wandelbots API

73 lines (64 loc) 2.19 kB
/** * Builds a NATS subject from an AsyncAPI-style channel address template * (e.g. `"{instance}.v2.cells.{cell}"`) by substituting each `{param}` * placeholder with the corresponding value from `params`. */ function isValidSubjectChar(char: string): boolean { const code = char.charCodeAt(0) return ( (code >= 48 && code <= 57) || // 0-9 (code >= 65 && code <= 90) || // A-Z (code >= 97 && code <= 122) || // a-z char === "-" || char === "_" ) } function isValidSubjectValue(value: string): boolean { // A bare "*" is the NATS single-token wildcard, allowed on its own (e.g. // subscribing to all cells with `{ cell: "*" }") but not as part of a // larger value, since it wouldn't act as a wildcard there anyway. if (value === "*") return true if (value.length === 0) return false for (const char of value) { if (!isValidSubjectChar(char)) return false } return true } export function buildSubject( template: string, params: Record<string, string>, ): string { // Scanned manually (rather than with a regex like /\{([^}]+)\}/g) to avoid // a polynomial-time backtracking blowup on pathological input, e.g. a // template consisting of many "{" characters with no closing "}". let result = "" let cursor = 0 while (cursor < template.length) { const openIndex = template.indexOf("{", cursor) if (openIndex === -1) { result += template.slice(cursor) break } const closeIndex = template.indexOf("}", openIndex + 1) if (closeIndex === -1) { result += template.slice(cursor) break } result += template.slice(cursor, openIndex) const paramName = template.slice(openIndex + 1, closeIndex) const value = params[paramName] if (value === undefined) { throw new Error( `Missing value for subject parameter "${paramName}" in template "${template}"`, ) } if (!isValidSubjectValue(value)) { throw new Error( `Invalid value for subject parameter "${paramName}": "${value}" (must be non-empty and contain only letters, digits, "-", and "_")`, ) } result += value cursor = closeIndex + 1 } return result }