@langchain/core
Version:
Core LangChain.js abstractions and schemas
1 lines • 6.14 kB
Source Map (JSON)
{"version":3,"file":"namespace.cjs","names":[],"sources":["../../src/utils/namespace.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * A constructor type that can be used as a base class for branding.\n */\ntype Constructor = abstract new (...args: any[]) => any;\n\n/**\n * The return type of `Namespace.brand()`: the original base class\n * intersected with a polymorphic `isInstance` type guard.\n */\ntype BrandedClass<TBase extends Constructor> = TBase & {\n isInstance: IsInstanceFn;\n};\n\n/**\n * A static `isInstance` type guard that resolves to the subclass it's\n * called on via the generic `this` parameter.\n *\n * At call sites TypeScript infers `T` from the class, so\n * `StandardError.isInstance(obj)` narrows `obj` to `StandardError`.\n */\ntype IsInstanceFn = <T extends Constructor>(\n this: T,\n obj: unknown\n) => obj is InstanceType<T>;\n\n/**\n * A namespace object for hierarchical symbol-based branding.\n *\n * Namespaces are internal authoring tools. They are not intended to be\n * part of the public API -- only the branded classes are.\n *\n * @example\n * ```typescript\n * const langchain = createNamespace(\"langchain\");\n * const errorNs = langchain.sub(\"error\");\n *\n * export class LangChainError extends errorNs.brand(Error) {\n * readonly name = \"LangChainError\";\n * }\n *\n * export class ModelAbortError extends errorNs.brand(LangChainError, \"model-abort\") {\n * readonly name = \"ModelAbortError\";\n * }\n * ```\n */\nexport interface Namespace {\n /**\n * Brand a base class with this namespace's symbols.\n *\n * Without a marker, creates a namespace-level base class whose\n * `static isInstance` checks for the namespace's own symbol.\n *\n * With a marker, creates a leaf class whose `static isInstance`\n * checks for the marker-specific symbol.\n *\n * @param Base - The base class to extend\n * @param marker - Optional marker for leaf classes\n * @returns A new class extending Base with symbol branding and static isInstance\n */\n brand<TBase extends Constructor>(\n Base: TBase,\n marker?: string\n ): BrandedClass<TBase>;\n\n /**\n * Create a child namespace.\n *\n * @param childPath - The child segment to append to the current path\n * @returns A new Namespace with the extended path\n */\n sub(childPath: string): Namespace;\n\n /**\n * Check if an object is branded under this namespace (at any level).\n *\n * @param obj - The object to check\n * @returns true if the object carries this namespace's symbol\n */\n isInstance(obj: unknown): boolean;\n}\n\n/**\n * Create a symbol-based namespace for hierarchical `isInstance` checking.\n *\n * Each namespace level gets its own `Symbol.for(path)`. When a class is\n * branded via `.brand()`, only the new symbol for that level is stamped\n * on the prototype. Parent symbols are inherited implicitly through the\n * class extension chain -- `symbol in obj` traverses the prototype chain,\n * so a `ConfigError` instance is recognized by `LangChainError.isInstance()`\n * because it extends `GoogleError` which extends `LangChainError`, whose\n * prototype already carries the `langchain.error` symbol.\n *\n * @param path - The dot-separated namespace path (e.g. \"langchain.error\")\n * @returns A Namespace object with `.brand()`, `.sub()`, and `.isInstance()`\n *\n * @example\n * ```typescript\n * const langchain = createNamespace(\"langchain\");\n * const errorNs = langchain.sub(\"error\");\n * const googleNs = errorNs.sub(\"google\");\n *\n * class LangChainError extends errorNs.brand(Error) {}\n * class GoogleError extends googleNs.brand(LangChainError) {}\n * class ConfigError extends googleNs.brand(GoogleError, \"configuration\") {}\n *\n * const err = new ConfigError(\"bad config\");\n * LangChainError.isInstance(err); // true (checks langchain.error symbol)\n * GoogleError.isInstance(err); // true (checks langchain.error.google symbol)\n * ConfigError.isInstance(err); // true (checks langchain.error.google.configuration symbol)\n * ```\n */\nexport function createNamespace(path: string): Namespace {\n const symbol: symbol = Symbol.for(path);\n\n return {\n brand<TBase extends Constructor>(Base: TBase, marker?: string) {\n const brandSymbol: symbol = marker\n ? Symbol.for(`${path}.${marker}`)\n : symbol;\n\n class _Branded extends (Base as any) {\n readonly [brandSymbol] = true as const;\n\n constructor(...args: any[]) {\n super(...args);\n }\n\n static isInstance(obj: unknown): boolean {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n brandSymbol in obj &&\n (obj as Record<symbol, unknown>)[brandSymbol] === true\n );\n }\n }\n\n // Inherit the base class's name so \"_Branded\" doesn't leak\n // through the static prototype chain.\n Object.defineProperty(_Branded, \"name\", { value: Base.name });\n\n return _Branded as unknown as BrandedClass<TBase>;\n },\n\n sub(childPath: string): Namespace {\n return createNamespace(`${path}.${childPath}`);\n },\n\n isInstance(obj: unknown): boolean {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n symbol in obj &&\n (obj as Record<symbol, unknown>)[symbol] === true\n );\n },\n };\n}\n\n/** Base namespace used throughout LangChain */\nexport const ns = createNamespace(\"langchain\");\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiHA,SAAgB,gBAAgB,MAAyB;CACvD,MAAM,SAAiB,OAAO,IAAI,KAAK;AAEvC,QAAO;EACL,MAAiC,MAAa,QAAiB;GAC7D,MAAM,cAAsB,SACxB,OAAO,IAAI,GAAG,KAAK,GAAG,SAAS,GAC/B;GAEJ,MAAM,iBAAkB,KAAa;IACnC,CAAU,eAAe;IAEzB,YAAY,GAAG,MAAa;AAC1B,WAAM,GAAG,KAAK;;IAGhB,OAAO,WAAW,KAAuB;AACvC,YACE,OAAO,QAAQ,YACf,QAAQ,QACR,eAAe,OACd,IAAgC,iBAAiB;;;AAOxD,UAAO,eAAe,UAAU,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;AAE7D,UAAO;;EAGT,IAAI,WAA8B;AAChC,UAAO,gBAAgB,GAAG,KAAK,GAAG,YAAY;;EAGhD,WAAW,KAAuB;AAChC,UACE,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACT,IAAgC,YAAY;;EAGlD;;;AAIH,MAAa,KAAK,gBAAgB,YAAY"}