/** * Exact path patterns for Media over QUIC: grammar, matching, and set algebra. * * A {@link Pattern} describes a set of broadcast paths. {@link Patterns} is an unordered * union reduced by exact containment. Matching is linear. {@link Pattern.literal} * rejects `*` because it is reserved for pattern syntax. * * `@moq/net` and `@moq/auth` re-export this package. The Rust twin is `moq-pattern`. * * ## Grammar * * A pattern is canonical `/`-separated segments: * * - a literal; * - `*`, matching one complete segment; * - `lit*lit`, with one `*` matching bytes inside one segment; * - `**`, matching zero or more complete segments, at most once per pattern. * * Patterns are exact: `foo` matches only `foo`, `foo/**` matches its subtree including * `foo`, `**` matches every path, and the empty pattern matches only the current root. * Parse rejects leading, trailing, or repeated `/`, more than one `*` in a segment, * `**` mixed with literal bytes, more than one `**`, and more than * {@link Pattern.MAX_SEGMENTS} (32) segments. Construction moves `**` before adjacent * `*` segments, so `* /**` is `** /*`. * * ## Algebra * * {@link Pattern.matches}, {@link Pattern.overlaps}, {@link Pattern.contains}, * {@link Pattern.head}, {@link Pattern.specificity}, set-valued {@link Pattern.rebase} and * {@link Pattern.intersect}, and {@link Pattern.captures}. A rebase never picks one lossy * residual: `** /a` at `a` is both the empty pattern and `** /a`, and an intersection never * picks one lossy overlap: `a/**` with `** /a` is both `a` and `a/** /a`. A union reduces * per member; a candidate covered only jointly by several members is refused. * * ## CAT / C4M * * Common Access Token and `draft-ietf-moq-c4m-01` match namespace fields positionally: * exact, prefix, or suffix per field, with a trailing `nil` for exact depth. Without * `nil`, longer namespaces that start with the matching fields are in scope. * * That common subset is `foo/bar` (exact fields plus `nil`), `foo/bar/**` (no `nil`), * `foo*` / `*foo` (prefix / suffix on a field), `*` (prefix of the empty byte string), * and `pid/ * /chat` (exact, any field, exact, `nil`). Richer MoQ forms, kept explicit * rather than claimed as CAT gaps: `**` not at the end (`** /a`, `a/** /b`) because C4M * is positional from the front, and `foo*bar` because a C4M match object is exact, * prefix, or suffix, not both. * * ## Literal paths * * `@moq/net`'s `Path` stays a coordinate. Roots, joins, exact names, URL paths, and * object-store keys keep their own types. {@link Pattern.literal} rejects `*`; literal * path construction and wire decoding retain their existing behavior. * * @module */ /** * One segment of a {@link Pattern}. * * A literal is never empty and never contains `/` or `*`. A wildcard (`*`) matches any * one segment. A partial (`prefix*suffix`) matches any one segment that starts with the * prefix and ends with the suffix without overlap; either may be empty, not both. A * globstar (`**`) matches any run of zero or more segments, at most once per pattern. */ export type Segment = { readonly kind: "literal"; readonly value: string; } | { readonly kind: "wildcard"; } | { readonly kind: "partial"; readonly prefix: string; readonly suffix: string; } | { readonly kind: "globstar"; }; /** Thrown when a pattern's text or segments violate the grammar. */ export declare class InvalidPattern extends Error { /** Which rule was broken. */ readonly code: InvalidPattern.Code; /** Create an error with the violated grammar rule in `code` and a human-readable `message`. */ constructor(code: InvalidPattern.Code, message: string); } export declare namespace InvalidPattern { /** Why a string or a segment list is not a valid {@link Pattern}. */ type Code = /** A segment is empty: a leading, trailing, or doubled `/`. */ "empty-segment" /** A segment's kind, fields, or wildcard syntax is invalid. */ | "invalid-segment" /** More than one `**`. */ | "multiple-globstars" /** More than {@link Pattern.MAX_SEGMENTS} segments. */ | "too-many-segments"; } /** Thrown when an exact intersection would produce too many patterns safely. */ export declare class IntersectionError extends Error { /** Create an intersection complexity error. */ constructor(); } /** * How much of a path a pattern pins down, for ranking the patterns that match one path. * * Compare with {@link compareSpecificity}. The order agrees with containment: when `a` * matches a strict superset of `b`'s paths, `a` ranks below `b`. Patterns that tie without * being equal (`* /a` and `* /b`) form one tier. */ export interface Specificity { /** Literal segments; more is more specific. */ readonly literals: number; /** Whether there is no `**`; exact beats not. */ readonly exact: boolean; /** Partial (`prefix*suffix`) segments; more is more specific. */ readonly partials: number; /** `*` segments; more is more specific. */ readonly wildcards: number; /** Bytes pinned by partial segments; more is more specific. */ readonly pinned: number; /** Leading literal segments; longer is more specific. */ readonly head: number; } /** Positive when `a` is more specific than `b`, negative when less, zero for one tier. */ export declare function compareSpecificity(a: Specificity, b: Specificity): number; /** * A pattern over broadcast paths: literal segments, `*` for one segment, `prefix*suffix` * for one segment with a known start and end, and at most one `**` for any run of * segments. Every segment kind matches whole segments, and a pattern is exact: `foo` * matches only `foo`, and a subtree is `foo/**`. * * Build one with {@link Pattern.parse}, {@link Pattern.from} (segments), or * {@link Pattern.literal} and {@link Pattern.subtree} (from a path). Two patterns are * {@link Pattern.equals | equal} when their text is, and the text is canonical: equal * patterns match the same paths, and only they do. Construction moves `**` before * adjacent `*` segments, so `* /**` prints as `** /*`. * * @public */ export declare class Pattern { #private; /** The most segments a pattern may have, matching the path limit on the wire. */ static get MAX_SEGMENTS(): number; /** The most patterns one exact intersection may produce. */ static get MAX_INTERSECTIONS(): number; /** The canonical text: segments joined by `/`, wildcards as `*` and `**`. */ readonly text: string; /** The segments, in order. */ readonly segments: readonly Segment[]; private constructor(); /** * Parse a pattern's text. Throws {@link InvalidPattern} on invalid syntax. * * Unlike a path, slashes are not normalized: a leading, trailing, or doubled `/` is * an error, so a typo cannot silently widen a grant. */ static parse(text: string): Pattern; /** A pattern from its segments, validating the grammar. Throws {@link InvalidPattern}. */ static from(segments: Iterable): Pattern; /** * The pattern matching exactly `path`. * * The path is normalized like a broadcast path (slashes trimmed and collapsed). * Throws when a segment is `*` or `**` or contains `*`: those are wildcards, and a * path using them cannot be named by a pattern. */ static literal(path: string): Pattern; /** * The pattern matching `path` and everything beneath it: `path/**`. * * The empty path yields `**`, and a path of {@link Pattern.MAX_SEGMENTS} yields the * literal, since nothing can sit beneath it. */ static subtree(path: string): Pattern; /** The pattern matching every path: `**`. */ static all(): Pattern; /** The empty pattern, which matches only the empty path. */ static empty(): Pattern; /** Order two patterns by text, so a sorted list is deterministic. */ static compare(a: Pattern, b: Pattern): number; /** The canonical text. */ toString(): string; /** Serializes as the canonical text, matching the Rust crate's serde form. */ toJSON(): string; /** Whether the two patterns are the same pattern. */ equals(other: Pattern): boolean; /** * The literal segments before the first wildcard, as a path. * * Every matching path starts with it, so it is where a tree walk starts. Empty when * the pattern starts with a wildcard; the whole pattern when it has none. */ get head(): string; /** Whether the pattern has no wildcards, so it matches exactly one path. */ get isLiteral(): boolean; /** The covered prefix for literals followed by `**`, or undefined for other patterns. */ asPrefix(): string | undefined; /** Whether the pattern has a `**`, so it matches paths of more than one length. */ get hasGlobstar(): boolean; /** Whether `path` is in the set this pattern describes. The path is normalized like a broadcast path. */ matches(path: string): boolean; /** * Whether every path `other` matches, this pattern matches too. * * This is the authorization check: a grant contains a request when the request * cannot name a path outside it. A pattern contains itself. */ contains(other: Pattern): boolean; /** Whether some path matches both patterns. */ overlaps(other: Pattern): boolean; /** How much of a path this pattern pins down. See {@link Specificity}. */ specificity(): Specificity; /** * The patterns that, relative to `root`, match exactly the paths this pattern matches * beneath `root`. * * This is how a grant or an advertisement is presented inside a rooted view. It is a * set because `**` may consume the root or stop short of it: `** /a` rebased at `a` is * both the empty pattern (the root itself) and `** /a` (deeper paths ending in `a`). * Empty when nothing under `root` matches. The root is normalized like a broadcast path. */ rebase(root: string): Patterns; /** * The patterns matching exactly the paths both patterns match. * * This is how a claim is clamped to a scope: the covered paths inside the grant, as * patterns of their own. It is a set because two partial segments or two `**` runs can * meet in more than one way: `ab*` and `*b` meet at `ab*b` and at `ab`, and `a/**` and * `** /a` meet at `a/** /a` and at `a`. Empty when the two do not {@link overlaps | overlap}. */ intersect(other: Pattern): Patterns; /** * What each wildcard of this pattern stands for in `matched`, a pattern this one * {@link contains}; undefined when it does not. * * One capture per non-literal segment (`*`, `prefix*suffix`, `**`), in order, the way a * regex match exposes its groups: `foo/* /chat` against `foo/alice/chat` captures `alice`, * and `foo/**` against `foo/alice/chat` captures `alice/chat`. A capture is a pattern * because `matched` may be one: `foo/**` against `foo/alice/**` captures `alice/**`. When * `matched` has a `**` that this pattern's own segments straddle (`** /*` against `a/**`, * where the last segment is `a` or anything after it), the segments it straddles cannot * be pinned and capture themselves: `**` then `*`. */ captures(matched: Pattern): Pattern[] | undefined; /** * This pattern placed beneath a literal `root`: the same paths, named from the root's * parent. The inverse of {@link rebase} for a single pattern. * * The root is normalized and validated like {@link Pattern.literal}, and the result * must fit {@link Pattern.MAX_SEGMENTS}. */ rooted(root: string): Pattern; } /** * A union of patterns, reduced so no member is contained by another. * * This is the shape of a grant (the paths a token may publish) and of a rebased pattern * (see {@link Pattern.rebase}). Members are kept in canonical order, so two unions * describing the same reduced set are {@link Patterns.equals | equal}. * * Containment is per member: {@link Patterns.contains} holds when one pattern in the union * contains the candidate. A candidate covered only jointly by several members (`a/**` * against `a`, `a/*`, and `a/* /**`) is refused, which keeps the check linear and its answer * easy to predict. A grant that means a subtree writes `a/**`. * * @public */ export declare class Patterns implements Iterable { #private; /** A union of the given patterns, reduced. Empty when none are given. */ constructor(patterns?: Iterable); /** * Add a pattern, dropping members it contains. * * Returns false when a member already contains it, leaving the union unchanged. */ insert(pattern: Pattern): boolean; /** Whether any member matches `path`. */ matches(path: string): boolean; /** Whether some member contains `pattern`. See the class docs for why this is per member. */ contains(pattern: Pattern): boolean; /** Whether every member of `other` is contained here: `other` grants nothing this union does not. */ covers(other: Patterns): boolean; /** Whether any member overlaps `pattern`. */ overlaps(pattern: Pattern): boolean; /** * The paths in both unions, as one union: every member of this one intersected with * every member of `other`. See {@link Pattern.intersect}. */ intersect(other: Patterns): Patterns; /** Every member rebased at `root`, as one union. See {@link Pattern.rebase}. */ rebase(root: string): Patterns; /** Every member placed beneath `root`. See {@link Pattern.rooted}. */ rooted(root: string): Patterns; /** The number of members. */ get size(): number; /** The members, in canonical order. */ [Symbol.iterator](): Iterator; /** The members as a fresh array, in canonical order. */ toArray(): Pattern[]; /** Serializes as a list of texts, matching the Rust crate's serde form. */ toJSON(): string[]; /** Whether the two unions have the same reduced pattern members. */ equals(other: Patterns): boolean; } //# sourceMappingURL=index.d.ts.map