@scalar/json-magic 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
1
 
2
- > @scalar/json-magic@0.3.0 build /home/runner/work/scalar/scalar/packages/json-magic
2
+ > @scalar/json-magic@0.4.0 build /home/runner/work/scalar/scalar/packages/json-magic
3
3
  > scalar-build-esbuild
4
4
 
5
- @scalar/json-magic: Build completed in 42.56ms
5
+ @scalar/json-magic: Build completed in 27.42ms
6
6
 
7
- > @scalar/json-magic@0.3.0 types:build /home/runner/work/scalar/scalar/packages/json-magic
7
+ > @scalar/json-magic@0.4.0 types:build /home/runner/work/scalar/scalar/packages/json-magic
8
8
  > scalar-types-build
9
9
 
10
- Types build completed in 1.52s
10
+ Types build completed in 1.60s
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @scalar/json-magic
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 99894bc: feat: correctly validate the schemas
8
+
9
+ ### Patch Changes
10
+
11
+ - 06a46f0: fix: add proxy cache to fix reactivity issues
12
+ - 63283aa: fix: use hidden properties during validation
13
+ - Updated dependencies [98c55d0]
14
+ - Updated dependencies [0e747c7]
15
+ - @scalar/helpers@0.0.9
16
+
17
+ ## 0.3.1
18
+
19
+ ### Patch Changes
20
+
21
+ - 88385b1: fix: external ref linking when starting with a /
22
+
3
23
  ## 0.3.0
4
24
 
5
25
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle.ts"],"names":[],"mappings":"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"}
@@ -63,6 +63,10 @@ function resolveReferencePath(base, relativePath) {
63
63
  }
64
64
  if (isRemoteUrl(base)) {
65
65
  const url = new URL(base);
66
+ if (relativePath.startsWith("/")) {
67
+ url.pathname = relativePath;
68
+ return url.toString();
69
+ }
66
70
  const mergedPath = path.join(path.dirname(url.pathname), relativePath);
67
71
  return new URL(mergedPath, base).toString();
68
72
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/bundle/bundle.ts"],
4
- "sourcesContent": ["import type { UnknownObject } from '@/types'\n\nimport { escapeJsonPointer } from '../utils/escape-json-pointer'\nimport path from '@/polyfills/path'\nimport { getSegmentsFromPath } from '../utils/get-segments-from-path'\nimport { isObject } from '../utils/is-object'\nimport { isYaml } from '../utils/is-yaml'\nimport { isJsonObject } from '../utils/is-json-object'\nimport { getHash, uniqueValueGeneratorFactory } from './value-generator'\n\n/**\n * Checks if a string is a remote URL (starts with http:// or https://)\n * @param value - The URL string to check\n * @returns true if the string is a remote URL, false otherwise\n * @example\n * ```ts\n * isRemoteUrl('https://example.com/schema.json') // true\n * isRemoteUrl('http://api.example.com/schemas/user.json') // true\n * isRemoteUrl('#/components/schemas/User') // false\n * isRemoteUrl('./local-schema.json') // false\n * ```\n */\nexport function isRemoteUrl(value: string) {\n try {\n const url = new URL(value)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * Checks if a string represents a file path by ensuring it's not a remote URL,\n * YAML content, or JSON content.\n *\n * @param value - The string to check\n * @returns true if the string appears to be a file path, false otherwise\n * @example\n * ```ts\n * isFilePath('./schemas/user.json') // true\n * isFilePath('https://example.com/schema.json') // false\n * isFilePath('{\"type\": \"object\"}') // false\n * isFilePath('type: object') // false\n * ```\n */\nexport function isFilePath(value: string) {\n return !isRemoteUrl(value) && !isYaml(value) && !isJsonObject(value)\n}\n\n/**\n * Checks if a string is a local reference (starts with #)\n * @param value - The reference string to check\n * @returns true if the string is a local reference, false otherwise\n * @example\n * ```ts\n * isLocalRef('#/components/schemas/User') // true\n * isLocalRef('https://example.com/schema.json') // false\n * isLocalRef('./local-schema.json') // false\n * ```\n */\nexport function isLocalRef(value: string): boolean {\n return value.startsWith('#')\n}\n\nexport type ResolveResult = { ok: true; data: unknown } | { ok: false }\n\n/**\n * Resolves a string by finding and executing the appropriate plugin.\n * @param value - The string to resolve (URL, file path, etc)\n * @param plugins - Array of plugins that can handle different types of strings\n * @returns A promise that resolves to either the content or an error result\n * @example\n * // Using a URL plugin\n * await resolveContents('https://example.com/schema.json', [urlPlugin])\n * // Using a file plugin\n * await resolveContents('./schemas/user.json', [filePlugin])\n * // No matching plugin returns { ok: false }\n * await resolveContents('#/components/schemas/User', [urlPlugin, filePlugin])\n */\nasync function resolveContents(value: string, plugins: LoaderPlugin[]): Promise<ResolveResult> {\n const plugin = plugins.find((p) => p.validate(value))\n\n if (plugin) {\n return plugin.exec(value)\n }\n\n return {\n ok: false,\n }\n}\n\n/**\n * Retrieves a nested value from an object using an array of property segments.\n * @param target - The target object to traverse\n * @param segments - Array of property names representing the path to the desired value\n * @returns The value at the specified path, or undefined if the path doesn't exist\n * @example\n * const obj = { foo: { bar: { baz: 42 } } };\n * getNestedValue(obj, ['foo', 'bar', 'baz']); // returns 42\n */\nexport function getNestedValue(target: Record<string, any>, segments: string[]) {\n return segments.reduce<any>((acc, key) => {\n if (acc === undefined) {\n return undefined\n }\n return acc[key]\n }, target)\n}\n\n/**\n * Sets a value at a specified path in an object, creating intermediate objects/arrays as needed.\n * This function traverses the object structure and creates any missing intermediate objects\n * or arrays based on the path segments. If the next segment is a numeric string, it creates\n * an array instead of an object.\n *\n * \u26A0\uFE0F Warning: Be careful with object keys that look like numbers (e.g. \"123\") as this function\n * will interpret them as array indices and create arrays instead of objects. If you need to\n * use numeric-looking keys, consider prefixing them with a non-numeric character.\n *\n * @param obj - The target object to set the value in\n * @param path - The JSON pointer path where the value should be set\n * @param value - The value to set at the specified path\n * @throws {Error} If attempting to set a value at the root path ('')\n *\n * @example\n * const obj = {}\n * setValueAtPath(obj, '/foo/bar/0', 'value')\n * // Result:\n * // {\n * // foo: {\n * // bar: ['value']\n * // }\n * // }\n *\n * @example\n * const obj = { existing: { path: 'old' } }\n * setValueAtPath(obj, '/existing/path', 'new')\n * // Result:\n * // {\n * // existing: {\n * // path: 'new'\n * // }\n * // }\n *\n * @example\n * // \u26A0\uFE0F Warning: This will create an array instead of an object with key \"123\"\n * setValueAtPath(obj, '/foo/123/bar', 'value')\n * // Result:\n * // {\n * // foo: [\n * // undefined,\n * // undefined,\n * // undefined,\n * // { bar: 'value' }\n * // ]\n * // }\n */\nexport function setValueAtPath(obj: any, path: string, value: any): void {\n if (path === '') {\n throw new Error(\"Cannot set value at root ('') pointer\")\n }\n\n const parts = getSegmentsFromPath(path)\n\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const key = parts[i]\n const isLast = i === parts.length - 1\n\n const nextKey = parts[i + 1]\n const shouldBeArray = /^\\d+$/.test(nextKey ?? '')\n\n if (isLast) {\n current[key] = value\n } else {\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = shouldBeArray ? [] : {}\n }\n current = current[key]\n }\n }\n}\n\n/**\n * Resolves a reference path by combining a base path with a relative path.\n * Handles both remote URLs and local file paths.\n *\n * @param base - The base path (can be a URL or local file path)\n * @param relativePath - The relative path to resolve against the base\n * @returns The resolved absolute path\n * @example\n * // Resolve remote URL\n * resolveReferencePath('https://example.com/api/schema.json', 'user.json')\n * // Returns: 'https://example.com/api/user.json'\n *\n * // Resolve local path\n * resolveReferencePath('/path/to/schema.json', 'user.json')\n * // Returns: '/path/to/user.json'\n */\nfunction resolveReferencePath(base: string, relativePath: string) {\n if (isRemoteUrl(relativePath)) {\n return relativePath\n }\n\n if (isRemoteUrl(base)) {\n const url = new URL(base)\n\n const mergedPath = path.join(path.dirname(url.pathname), relativePath)\n return new URL(mergedPath, base).toString()\n }\n\n return path.join(path.dirname(base), relativePath)\n}\n\n/**\n * Prefixes an internal JSON reference with a given path prefix.\n * Takes a local reference (starting with #) and prepends the provided prefix segments.\n *\n * @param input - The internal reference string to prefix (must start with #)\n * @param prefix - Array of path segments to prepend to the reference\n * @returns The prefixed reference string\n * @throws Error if input is not a local reference\n * @example\n * prefixInternalRef('#/components/schemas/User', ['definitions'])\n * // Returns: '#/definitions/components/schemas/User'\n */\nexport function prefixInternalRef(input: string, prefix: string[]) {\n if (!isLocalRef(input)) {\n throw 'Please provide an internal ref'\n }\n\n return `#/${prefix.map(escapeJsonPointer).join('/')}${input.substring(1)}`\n}\n\n/**\n * Updates internal references in an object by adding a prefix to their paths.\n * Recursively traverses the input object and modifies any local $ref references\n * by prepending the given prefix to their paths. This is used when embedding external\n * documents to maintain correct reference paths relative to the main document.\n *\n * @param input - The object to update references in\n * @param prefix - Array of path segments to prepend to internal reference paths\n * @returns void\n * @example\n * ```ts\n * const input = {\n * foo: {\n * $ref: '#/components/schemas/User'\n * }\n * }\n * prefixInternalRefRecursive(input, ['definitions'])\n * // Result:\n * // {\n * // foo: {\n * // $ref: '#/definitions/components/schemas/User'\n * // }\n * // }\n * ```\n */\nexport function prefixInternalRefRecursive(input: unknown, prefix: string[]) {\n if (!isObject(input)) {\n return\n }\n\n Object.values(input).forEach((el) => prefixInternalRefRecursive(el, prefix))\n\n if (typeof input === 'object' && '$ref' in input && typeof input['$ref'] === 'string') {\n const ref = input['$ref']\n\n if (!isLocalRef(ref)) {\n return\n }\n\n input['$ref'] = prefixInternalRef(ref, prefix)\n }\n}\n\n/**\n * Resolves and copies referenced values from a source document to a target document.\n * This function traverses the document and copies referenced values to the target document,\n * while tracking processed references to avoid duplicates. It only processes references\n * that belong to the same external document.\n *\n * @param targetDocument - The document to copy referenced values to\n * @param sourceDocument - The source document containing the references\n * @param referencePath - The JSON pointer path to the reference\n * @param externalRefsKey - The key used for external references (e.g. 'x-ext')\n * @param documentKey - The key identifying the external document\n * @param processedNodes - Set of already processed nodes to prevent duplicates\n * @example\n * ```ts\n * const source = {\n * components: {\n * schemas: {\n * User: {\n * $ref: '#/x-ext/users~1schema/definitions/Person'\n * }\n * }\n * }\n * }\n *\n * const target = {}\n * resolveAndCopyReferences(\n * target,\n * source,\n * '/components/schemas/User',\n * 'x-ext',\n * 'users/schema'\n * )\n * // Result: target will contain the User schema with resolved references\n * ```\n */\nconst resolveAndCopyReferences = (\n targetDocument: unknown,\n sourceDocument: unknown,\n referencePath: string,\n externalRefsKey: string,\n documentKey: string,\n processedNodes = new Set(),\n) => {\n const referencedValue = getNestedValue(sourceDocument, getSegmentsFromPath(referencePath))\n\n if (processedNodes.has(referencedValue)) {\n return\n }\n processedNodes.add(referencedValue)\n\n setValueAtPath(targetDocument, referencePath, referencedValue)\n\n // Do the same for each local ref\n const traverse = (node: unknown) => {\n if (!node || typeof node !== 'object') {\n return\n }\n\n if ('$ref' in node && typeof node['$ref'] === 'string') {\n // We only process references from the same external document because:\n // 1. Other documents will be handled in separate recursive branches\n // 2. The source document only contains the current document's content\n // This prevents undefined behavior and maintains proper document boundaries\n if (node['$ref'].startsWith(`#/${externalRefsKey}/${escapeJsonPointer(documentKey)}`)) {\n resolveAndCopyReferences(\n targetDocument,\n sourceDocument,\n node['$ref'].substring(1),\n documentKey,\n externalRefsKey,\n processedNodes,\n )\n }\n }\n\n for (const value of Object.values(node)) {\n traverse(value)\n }\n }\n\n traverse(referencedValue)\n}\n\n/**\n * A loader plugin for resolving external references during bundling.\n * Loader plugins are responsible for handling specific types of external references,\n * such as files, URLs, or custom protocols. Each loader plugin must provide:\n *\n * - A `validate` function to determine if the plugin can handle a given reference string.\n * - An `exec` function to asynchronously fetch and resolve the referenced data,\n * returning a Promise that resolves to a `ResolveResult`.\n *\n * Loader plugins enable extensible support for different reference sources in the bundler.\n *\n * @property type - The plugin type, always 'loader' for loader plugins.\n * @property validate - Function to check if the plugin can handle a given reference value.\n * @property exec - Function to fetch and resolve the reference, returning the resolved data.\n */\nexport type LoaderPlugin = {\n type: 'loader'\n // Returns true if this plugin can handle the given reference value\n validate: (value: string) => boolean\n // Asynchronously fetches and resolves the reference, returning the resolved data\n exec: (value: string) => Promise<ResolveResult>\n}\n\n/**\n * Context information for a node during traversal or processing.\n *\n * Note: The `path` parameter represents the path to the current node being processed.\n * If you are performing a partial bundle (i.e., providing a custom root), this path will be relative\n * to the root you provide, not the absolute root of the original document. You may need to prefix\n * it with your own base path if you want to construct a full path from the absolute document root.\n *\n * - `path`: The JSON pointer path (as an array of strings) from the document root to the current node.\n * - `resolutionCache`: A cache for storing promises of resolved references.\n */\ntype NodeProcessContext = {\n path: readonly string[]\n resolutionCache: Map<string, Promise<Readonly<ResolveResult>>>\n parentNode: UnknownObject | null\n rootNode: UnknownObject\n loaders: LoaderPlugin[]\n}\n\n/**\n * A plugin type for lifecycle hooks, allowing custom logic to be injected into the bundler's process.\n * This type extends the Config['hooks'] interface and is identified by type: 'lifecycle'.\n */\nexport type LifecyclePlugin = { type: 'lifecycle' } & Config['hooks']\n\n/**\n * Represents a plugin used by the bundler for extensibility.\n *\n * Plugins can be either:\n * - Loader plugins: Responsible for resolving and loading external references (e.g., from files, URLs, or custom sources).\n * - Lifecycle plugins: Provide lifecycle hooks to customize or extend the bundling process.\n *\n * Loader plugins must implement:\n * - `validate`: Checks if the plugin can handle a given reference value.\n * - `exec`: Asynchronously resolves and returns the referenced data.\n *\n * Lifecycle plugins extend the bundler's lifecycle hooks for custom logic.\n */\nexport type Plugin = LoaderPlugin | LifecyclePlugin\n\n/**\n * Configuration options for the bundler.\n * Controls how external references are resolved and processed during bundling.\n */\ntype Config = {\n /**\n * Array of plugins that handle resolving references from different sources.\n * Each plugin is responsible for fetching and processing data from specific sources\n * like URLs or the filesystem.\n */\n plugins: Plugin[]\n\n /**\n * Optional root object that serves as the base document when bundling a subpart.\n * This allows resolving references relative to the root document's location,\n * ensuring proper path resolution for nested references.\n */\n root?: UnknownObject\n\n /**\n * Optional maximum depth for reference resolution.\n * Limits how deeply the bundler will follow and resolve nested $ref pointers.\n * Useful for preventing infinite recursion or excessive resource usage.\n */\n depth?: number\n\n /**\n * Optional origin path for the bundler.\n * Used to resolve relative paths in references, especially when the input is a string URL or file path.\n * If not provided, the bundler will use the input value as the origin.\n */\n origin?: string\n\n /**\n * Optional cache to store promises of resolved references.\n * Helps avoid duplicate fetches/reads of the same resource by storing\n * the resolution promises for reuse.\n */\n cache?: Map<string, Promise<ResolveResult>>\n\n /**\n * Cache of visited nodes during partial bundling.\n * Used to prevent re-bundling the same tree multiple times when doing partial bundling,\n * improving performance by avoiding redundant processing of already bundled sections.\n */\n visitedNodes?: Set<unknown>\n\n /**\n * Enable tree shaking to optimize the bundle size.\n * When enabled, only the parts of external documents that are actually referenced\n * will be included in the final bundle.\n */\n treeShake: boolean\n\n /**\n * Optional flag to generate a URL map.\n * When enabled, tracks the original source URLs of bundled references\n * in an x-ext-urls section for reference mapping.\n */\n urlMap?: boolean\n\n /**\n * Optional function to compress input URLs or file paths before bundling.\n * Returns either a Promise resolving to the compressed string or the compressed string directly.\n */\n compress?: (value: string) => Promise<string> | string\n\n /**\n * Optional hooks to monitor the bundler's lifecycle.\n * Allows tracking the progress and status of reference resolution.\n */\n hooks?: Partial<{\n /**\n * Optional hook called when the bundler starts resolving a $ref.\n * Useful for tracking or logging the beginning of a reference resolution.\n */\n onResolveStart: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler fails to resolve a $ref.\n * Can be used for error handling, logging, or custom error reporting.\n */\n onResolveError: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler successfully resolves a $ref.\n * Useful for tracking successful resolutions or custom post-processing.\n */\n onResolveSuccess: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook invoked before processing a node.\n * Can be used for preprocessing, mutation, or custom logic before the node is handled by the bundler.\n */\n onBeforeNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n /**\n * Optional hook invoked after processing a node.\n * Useful for postprocessing, cleanup, or custom logic after the node has been handled by the bundler.\n */\n onAfterNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n }>\n}\n\n/**\n * Extension keys used for bundling external references in OpenAPI documents.\n * These custom extensions help maintain the structure and traceability of bundled documents.\n */\nconst extensions = {\n /**\n * Custom OpenAPI extension key used to store external references.\n * This key will contain all bundled external documents.\n * The x-ext key is used to maintain a clean separation between the main\n * OpenAPI document and its bundled external references.\n */\n externalDocuments: 'x-ext',\n\n /**\n * Custom OpenAPI extension key used to maintain a mapping between\n * hashed keys and their original URLs in x-ext.\n * This mapping is essential for tracking the source of bundled references\n */\n externalDocumentsMappings: 'x-ext-urls',\n} as const\n\n/**\n * Bundles an OpenAPI specification by resolving all external references.\n * This function traverses the input object recursively and embeds external $ref\n * references into an x-ext section. External references can be URLs or local files.\n * The original $refs are updated to point to their embedded content in the x-ext section.\n * If the input is an object, it will be modified in place by adding an x-ext\n * property to store resolved external references.\n *\n * @param input - The OpenAPI specification to bundle. Can be either an object or string.\n * If a string is provided, it will be resolved using the provided plugins.\n * If no plugin can process the input, the onReferenceError hook will be invoked\n * and an error will be emitted to the console.\n * @param config - Configuration object containing plugins and options for bundling OpenAPI specifications\n * @returns A promise that resolves to the bundled specification with all references embedded\n * @example\n * // Example with object input\n * const spec = {\n * paths: {\n * '/users': {\n * $ref: 'https://example.com/schemas/users.yaml'\n * }\n * }\n * }\n *\n * const bundled = await bundle(spec, {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // Result:\n * // {\n * // paths: {\n * // '/users': {\n * // $ref: '#/x-ext/abc123'\n * // }\n * // },\n * // 'x-ext': {\n * // 'abc123': {\n * // // Resolved content from users.yaml\n * // }\n * // },\n * // 'x-ext-urls': {\n * // 'https://example.com/schemas/users.yaml': 'abc123'\n * // }\n * // }\n *\n * // Example with URL input\n * const bundledFromUrl = await bundle('https://example.com/openapi.yaml', {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // The function will first fetch the OpenAPI spec from the URL,\n * // then bundle all its external references into the x-ext section\n */\nexport async function bundle(input: UnknownObject | string, config: Config) {\n // Cache for storing promises of resolved external references (URLs and local files)\n // to avoid duplicate fetches/reads of the same resource\n const cache = config.cache ?? new Map<string, Promise<ResolveResult>>()\n\n const loaderPlugins = config.plugins.filter((it) => it.type === 'loader')\n const lifecyclePlugin = config.plugins.filter((it) => it.type === 'lifecycle')\n\n /**\n * Resolves the input value by either returning it directly if it's not a string,\n * or attempting to resolve it using the provided plugins if it is a string.\n * @returns The resolved input data or throws an error if resolution fails\n */\n const resolveInput = async () => {\n if (typeof input !== 'string') {\n return input\n }\n const result = await resolveContents(input, loaderPlugins)\n\n if (result.ok && typeof result.data === 'object') {\n return result.data\n }\n\n throw new Error(\n 'Failed to resolve input: Please provide a valid string value or pass a loader to process the input',\n )\n }\n\n // Resolve the input specification, which could be either a direct object or a string URL/path\n const rawSpecification = await resolveInput()\n\n // Document root used to write all external documents\n // We need this when we want to do a partial bundle of a document\n const documentRoot = config.root ?? rawSpecification\n\n // Determines if the bundling operation is partial.\n // Partial bundling occurs when:\n // - A root document is provided that is different from the raw specification being bundled, or\n // - A maximum depth is specified in the config.\n // In these cases, only a subset of the document may be bundled.\n const isPartialBundling =\n (config.root !== undefined && config.root !== rawSpecification) || config.depth !== undefined\n\n // Set of nodes that have already been processed during bundling to prevent duplicate processing\n const processedNodes = config.visitedNodes ?? new Set()\n\n // Determines the initial origin path for the bundler based on the input type.\n // For string inputs that are URLs or file paths, uses the input as the origin.\n // For non-string inputs or other string types, returns an empty string.\n const defaultOrigin = () => {\n if (config.origin) {\n return config.origin\n }\n\n if (typeof input !== 'string') {\n return ''\n }\n\n if (isRemoteUrl(input) || isFilePath(input)) {\n return input\n }\n\n return ''\n }\n\n // Create the cache to store the compressed values to their map values\n if (documentRoot[extensions.externalDocumentsMappings] === undefined) {\n documentRoot[extensions.externalDocumentsMappings] = {}\n }\n const { generate } = uniqueValueGeneratorFactory(\n config.compress ?? getHash,\n documentRoot[extensions.externalDocumentsMappings],\n )\n\n const bundler = async (\n root: unknown,\n origin: string = defaultOrigin(),\n isChunkParent = false,\n depth = 0,\n path: readonly string[] = [],\n parent: UnknownObject = null,\n ) => {\n // If a maximum depth is set in the config, stop bundling when the current depth reaches or exceeds it\n if (config.depth !== undefined && depth > config.depth) {\n return\n }\n\n if (!isObject(root) && !Array.isArray(root)) {\n return\n }\n\n // Skip if this node has already been processed to prevent infinite recursion\n // and duplicate processing of the same node\n if (processedNodes.has(root)) {\n return\n }\n // Mark this node as processed before continuing\n processedNodes.add(root)\n\n // Invoke the onBeforeNodeProcess hook for the current node before any further processing\n await config.hooks?.onBeforeNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n // Invoke onBeforeNodeProcess hooks from all registered lifecycle plugins\n for (const plugin of lifecyclePlugin) {\n await plugin.onBeforeNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n }\n\n if (typeof root === 'object' && '$ref' in root && typeof root['$ref'] === 'string') {\n const ref = root['$ref']\n const isChunk = '$global' in root && typeof root['$global'] === 'boolean' && root['$global']\n\n if (isLocalRef(ref)) {\n if (isPartialBundling) {\n const segments = getSegmentsFromPath(ref.substring(1))\n const parent = segments.length > 0 ? getNestedValue(documentRoot, segments.slice(0, -1)) : undefined\n\n // When doing partial bundling, we need to recursively bundle all dependencies\n // referenced by this local reference to ensure the partial bundle is complete.\n // This includes not just the direct reference but also all its dependencies,\n // creating a complete and self-contained partial bundle.\n await bundler(getNestedValue(documentRoot, segments), origin, isChunkParent, depth + 1, segments, parent)\n }\n return\n }\n\n const [prefix, path = ''] = ref.split('#', 2)\n\n // Combine the current origin with the new path to resolve relative references\n // correctly within the context of the external file being processed\n const resolvedPath = resolveReferencePath(origin, prefix)\n\n // Generate a unique compressed path for the external document\n // This is used as a key to store and reference the bundled external document\n // The compression helps reduce the overall file size of the bundled document\n const compressedPath = await generate(resolvedPath)\n\n const seen = cache.has(resolvedPath)\n\n if (!seen) {\n cache.set(resolvedPath, resolveContents(resolvedPath, loaderPlugins))\n }\n\n config?.hooks?.onResolveStart?.(root)\n lifecyclePlugin.forEach((it) => it.onResolveStart?.(root))\n\n // Resolve the remote document\n const result = await cache.get(resolvedPath)\n\n if (result.ok) {\n // Process the result only once to avoid duplicate processing and prevent multiple prefixing\n // of internal references, which would corrupt the reference paths\n if (!seen) {\n // Skip prefixing for chunks since they are meant to be self-contained and their\n // internal references should remain relative to their original location. Chunks\n // are typically used for modular components that need to maintain their own\n // reference context without being affected by the main document's structure.\n if (!isChunk) {\n // Update internal references in the resolved document to use the correct base path.\n // When we embed external documents, their internal references need to be updated to\n // maintain the correct path context relative to the main document. This is crucial\n // because internal references in the external document are relative to its original\n // location, but when embedded, they need to be relative to their new location in\n // the main document's x-ext section. Without this update, internal references\n // would point to incorrect locations and break the document structure.\n prefixInternalRefRecursive(result.data, [extensions.externalDocuments, compressedPath])\n }\n\n // Recursively process the resolved content\n // to handle any nested references it may contain. We pass the resolvedPath as the new origin\n // to ensure any relative references within this content are resolved correctly relative to\n // their new location in the bundled document.\n await bundler(result.data, isChunk ? origin : resolvedPath, isChunk, depth + 1, [\n extensions.externalDocuments,\n compressedPath,\n documentRoot[extensions.externalDocumentsMappings],\n ])\n\n // Store the mapping between hashed keys and original URLs in x-ext-urls\n // This allows tracking which external URLs were bundled and their corresponding locations\n setValueAtPath(\n documentRoot,\n `/${extensions.externalDocumentsMappings}/${escapeJsonPointer(compressedPath)}`,\n resolvedPath,\n )\n }\n\n if (config.treeShake === true) {\n // Store only the subtree that is actually used\n // This optimizes the bundle size by only including the parts of the external document\n // that are referenced, rather than the entire document\n resolveAndCopyReferences(\n documentRoot,\n { [extensions.externalDocuments]: { [compressedPath]: result.data } },\n prefixInternalRef(`#${path}`, [extensions.externalDocuments, compressedPath]).substring(1),\n extensions.externalDocuments,\n compressedPath,\n )\n } else if (!seen) {\n // Store the external document in the main document's x-ext key\n // When tree shaking is disabled, we include the entire external document\n // This preserves all content and is faster since we don't need to analyze and copy\n // specific parts. This approach is ideal when storing the result in memory\n // as it avoids the overhead of tree shaking operations\n setValueAtPath(documentRoot, `/${extensions.externalDocuments}/${compressedPath}`, result.data)\n }\n\n // Update the $ref to point to the embedded document in x-ext\n // This is necessary because we need to maintain the correct path context\n // for the embedded document while preserving its internal structure\n root.$ref = prefixInternalRef(`#${path}`, [extensions.externalDocuments, compressedPath])\n\n config?.hooks?.onResolveSuccess?.(root)\n lifecyclePlugin.forEach((it) => it.onResolveSuccess?.(root))\n\n return\n }\n\n config?.hooks?.onResolveError?.(root)\n lifecyclePlugin.forEach((it) => it.onResolveError?.(root))\n\n return console.warn(\n `Failed to resolve external reference \"${resolvedPath}\". The reference may be invalid, inaccessible, or missing a loader for this type of reference.`,\n )\n }\n\n // Recursively traverse all child properties of the current object to resolve nested $ref references.\n // This step ensures that any $refs located deeper within the object hierarchy are discovered and processed.\n // We explicitly skip the extension keys (x-ext and x-ext-urls) to avoid reprocessing already bundled or mapped content.\n await Promise.all(\n Object.entries(root).map(async ([key, value]) => {\n if (key === extensions.externalDocuments || key === extensions.externalDocumentsMappings) {\n return\n }\n\n await bundler(value, origin, isChunkParent, depth + 1, [...path, key], root as UnknownObject)\n }),\n )\n\n // Invoke the optional onAfterNodeProcess hook from the config, if provided.\n // This allows for custom post-processing logic after a node has been handled by the bundler.\n await config.hooks?.onAfterNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n\n // Iterate through all lifecycle plugins and invoke their onAfterNodeProcess hooks, if defined.\n // This enables plugins to perform additional post-processing or cleanup after the node is processed.\n for (const plugin of lifecyclePlugin) {\n await plugin.onAfterNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n }\n }\n\n await bundler(rawSpecification)\n\n // Keep urlMappings when doing partial bundling to track hash values and handle collisions\n // For full bundling without urlMap config, remove the mappings to clean up the output\n if (!config.urlMap && !isPartialBundling) {\n // Remove the external document mappings from the output when doing a full bundle without urlMap config\n delete documentRoot[extensions.externalDocumentsMappings]\n }\n\n return rawSpecification\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { UnknownObject } from '@/types'\n\nimport { escapeJsonPointer } from '../utils/escape-json-pointer'\nimport path from '@/polyfills/path'\nimport { getSegmentsFromPath } from '../utils/get-segments-from-path'\nimport { isObject } from '../utils/is-object'\nimport { isYaml } from '../utils/is-yaml'\nimport { isJsonObject } from '../utils/is-json-object'\nimport { getHash, uniqueValueGeneratorFactory } from './value-generator'\n\n/**\n * Checks if a string is a remote URL (starts with http:// or https://)\n * @param value - The URL string to check\n * @returns true if the string is a remote URL, false otherwise\n * @example\n * ```ts\n * isRemoteUrl('https://example.com/schema.json') // true\n * isRemoteUrl('http://api.example.com/schemas/user.json') // true\n * isRemoteUrl('#/components/schemas/User') // false\n * isRemoteUrl('./local-schema.json') // false\n * ```\n */\nexport function isRemoteUrl(value: string) {\n try {\n const url = new URL(value)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * Checks if a string represents a file path by ensuring it's not a remote URL,\n * YAML content, or JSON content.\n *\n * @param value - The string to check\n * @returns true if the string appears to be a file path, false otherwise\n * @example\n * ```ts\n * isFilePath('./schemas/user.json') // true\n * isFilePath('https://example.com/schema.json') // false\n * isFilePath('{\"type\": \"object\"}') // false\n * isFilePath('type: object') // false\n * ```\n */\nexport function isFilePath(value: string) {\n return !isRemoteUrl(value) && !isYaml(value) && !isJsonObject(value)\n}\n\n/**\n * Checks if a string is a local reference (starts with #)\n * @param value - The reference string to check\n * @returns true if the string is a local reference, false otherwise\n * @example\n * ```ts\n * isLocalRef('#/components/schemas/User') // true\n * isLocalRef('https://example.com/schema.json') // false\n * isLocalRef('./local-schema.json') // false\n * ```\n */\nexport function isLocalRef(value: string): boolean {\n return value.startsWith('#')\n}\n\nexport type ResolveResult = { ok: true; data: unknown } | { ok: false }\n\n/**\n * Resolves a string by finding and executing the appropriate plugin.\n * @param value - The string to resolve (URL, file path, etc)\n * @param plugins - Array of plugins that can handle different types of strings\n * @returns A promise that resolves to either the content or an error result\n * @example\n * // Using a URL plugin\n * await resolveContents('https://example.com/schema.json', [urlPlugin])\n * // Using a file plugin\n * await resolveContents('./schemas/user.json', [filePlugin])\n * // No matching plugin returns { ok: false }\n * await resolveContents('#/components/schemas/User', [urlPlugin, filePlugin])\n */\nasync function resolveContents(value: string, plugins: LoaderPlugin[]): Promise<ResolveResult> {\n const plugin = plugins.find((p) => p.validate(value))\n\n if (plugin) {\n return plugin.exec(value)\n }\n\n return {\n ok: false,\n }\n}\n\n/**\n * Retrieves a nested value from an object using an array of property segments.\n * @param target - The target object to traverse\n * @param segments - Array of property names representing the path to the desired value\n * @returns The value at the specified path, or undefined if the path doesn't exist\n * @example\n * const obj = { foo: { bar: { baz: 42 } } };\n * getNestedValue(obj, ['foo', 'bar', 'baz']); // returns 42\n */\nexport function getNestedValue(target: Record<string, any>, segments: string[]) {\n return segments.reduce<any>((acc, key) => {\n if (acc === undefined) {\n return undefined\n }\n return acc[key]\n }, target)\n}\n\n/**\n * Sets a value at a specified path in an object, creating intermediate objects/arrays as needed.\n * This function traverses the object structure and creates any missing intermediate objects\n * or arrays based on the path segments. If the next segment is a numeric string, it creates\n * an array instead of an object.\n *\n * \u26A0\uFE0F Warning: Be careful with object keys that look like numbers (e.g. \"123\") as this function\n * will interpret them as array indices and create arrays instead of objects. If you need to\n * use numeric-looking keys, consider prefixing them with a non-numeric character.\n *\n * @param obj - The target object to set the value in\n * @param path - The JSON pointer path where the value should be set\n * @param value - The value to set at the specified path\n * @throws {Error} If attempting to set a value at the root path ('')\n *\n * @example\n * const obj = {}\n * setValueAtPath(obj, '/foo/bar/0', 'value')\n * // Result:\n * // {\n * // foo: {\n * // bar: ['value']\n * // }\n * // }\n *\n * @example\n * const obj = { existing: { path: 'old' } }\n * setValueAtPath(obj, '/existing/path', 'new')\n * // Result:\n * // {\n * // existing: {\n * // path: 'new'\n * // }\n * // }\n *\n * @example\n * // \u26A0\uFE0F Warning: This will create an array instead of an object with key \"123\"\n * setValueAtPath(obj, '/foo/123/bar', 'value')\n * // Result:\n * // {\n * // foo: [\n * // undefined,\n * // undefined,\n * // undefined,\n * // { bar: 'value' }\n * // ]\n * // }\n */\nexport function setValueAtPath(obj: any, path: string, value: any): void {\n if (path === '') {\n throw new Error(\"Cannot set value at root ('') pointer\")\n }\n\n const parts = getSegmentsFromPath(path)\n\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const key = parts[i]\n const isLast = i === parts.length - 1\n\n const nextKey = parts[i + 1]\n const shouldBeArray = /^\\d+$/.test(nextKey ?? '')\n\n if (isLast) {\n current[key] = value\n } else {\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = shouldBeArray ? [] : {}\n }\n current = current[key]\n }\n }\n}\n\n/**\n * Resolves a reference path by combining a base path with a relative path.\n * Handles both remote URLs and local file paths.\n *\n * @param base - The base path (can be a URL or local file path)\n * @param relativePath - The relative path to resolve against the base\n * @returns The resolved absolute path\n * @example\n * // Resolve remote URL\n * resolveReferencePath('https://example.com/api/schema.json', 'user.json')\n * // Returns: 'https://example.com/api/user.json'\n *\n * // Resolve local path\n * resolveReferencePath('/path/to/schema.json', 'user.json')\n * // Returns: '/path/to/user.json'\n */\nfunction resolveReferencePath(base: string, relativePath: string) {\n if (isRemoteUrl(relativePath)) {\n return relativePath\n }\n\n if (isRemoteUrl(base)) {\n const url = new URL(base)\n\n // If the url stars with a / we want it to resolve from the origin so we replace the pathname\n if (relativePath.startsWith('/')) {\n url.pathname = relativePath\n return url.toString()\n }\n\n const mergedPath = path.join(path.dirname(url.pathname), relativePath)\n return new URL(mergedPath, base).toString()\n }\n\n return path.join(path.dirname(base), relativePath)\n}\n\n/**\n * Prefixes an internal JSON reference with a given path prefix.\n * Takes a local reference (starting with #) and prepends the provided prefix segments.\n *\n * @param input - The internal reference string to prefix (must start with #)\n * @param prefix - Array of path segments to prepend to the reference\n * @returns The prefixed reference string\n * @throws Error if input is not a local reference\n * @example\n * prefixInternalRef('#/components/schemas/User', ['definitions'])\n * // Returns: '#/definitions/components/schemas/User'\n */\nexport function prefixInternalRef(input: string, prefix: string[]) {\n if (!isLocalRef(input)) {\n throw 'Please provide an internal ref'\n }\n\n return `#/${prefix.map(escapeJsonPointer).join('/')}${input.substring(1)}`\n}\n\n/**\n * Updates internal references in an object by adding a prefix to their paths.\n * Recursively traverses the input object and modifies any local $ref references\n * by prepending the given prefix to their paths. This is used when embedding external\n * documents to maintain correct reference paths relative to the main document.\n *\n * @param input - The object to update references in\n * @param prefix - Array of path segments to prepend to internal reference paths\n * @returns void\n * @example\n * ```ts\n * const input = {\n * foo: {\n * $ref: '#/components/schemas/User'\n * }\n * }\n * prefixInternalRefRecursive(input, ['definitions'])\n * // Result:\n * // {\n * // foo: {\n * // $ref: '#/definitions/components/schemas/User'\n * // }\n * // }\n * ```\n */\nexport function prefixInternalRefRecursive(input: unknown, prefix: string[]) {\n if (!isObject(input)) {\n return\n }\n\n Object.values(input).forEach((el) => prefixInternalRefRecursive(el, prefix))\n\n if (typeof input === 'object' && '$ref' in input && typeof input['$ref'] === 'string') {\n const ref = input['$ref']\n\n if (!isLocalRef(ref)) {\n return\n }\n\n input['$ref'] = prefixInternalRef(ref, prefix)\n }\n}\n\n/**\n * Resolves and copies referenced values from a source document to a target document.\n * This function traverses the document and copies referenced values to the target document,\n * while tracking processed references to avoid duplicates. It only processes references\n * that belong to the same external document.\n *\n * @param targetDocument - The document to copy referenced values to\n * @param sourceDocument - The source document containing the references\n * @param referencePath - The JSON pointer path to the reference\n * @param externalRefsKey - The key used for external references (e.g. 'x-ext')\n * @param documentKey - The key identifying the external document\n * @param processedNodes - Set of already processed nodes to prevent duplicates\n * @example\n * ```ts\n * const source = {\n * components: {\n * schemas: {\n * User: {\n * $ref: '#/x-ext/users~1schema/definitions/Person'\n * }\n * }\n * }\n * }\n *\n * const target = {}\n * resolveAndCopyReferences(\n * target,\n * source,\n * '/components/schemas/User',\n * 'x-ext',\n * 'users/schema'\n * )\n * // Result: target will contain the User schema with resolved references\n * ```\n */\nconst resolveAndCopyReferences = (\n targetDocument: unknown,\n sourceDocument: unknown,\n referencePath: string,\n externalRefsKey: string,\n documentKey: string,\n processedNodes = new Set(),\n) => {\n const referencedValue = getNestedValue(sourceDocument, getSegmentsFromPath(referencePath))\n\n if (processedNodes.has(referencedValue)) {\n return\n }\n processedNodes.add(referencedValue)\n\n setValueAtPath(targetDocument, referencePath, referencedValue)\n\n // Do the same for each local ref\n const traverse = (node: unknown) => {\n if (!node || typeof node !== 'object') {\n return\n }\n\n if ('$ref' in node && typeof node['$ref'] === 'string') {\n // We only process references from the same external document because:\n // 1. Other documents will be handled in separate recursive branches\n // 2. The source document only contains the current document's content\n // This prevents undefined behavior and maintains proper document boundaries\n if (node['$ref'].startsWith(`#/${externalRefsKey}/${escapeJsonPointer(documentKey)}`)) {\n resolveAndCopyReferences(\n targetDocument,\n sourceDocument,\n node['$ref'].substring(1),\n documentKey,\n externalRefsKey,\n processedNodes,\n )\n }\n }\n\n for (const value of Object.values(node)) {\n traverse(value)\n }\n }\n\n traverse(referencedValue)\n}\n\n/**\n * A loader plugin for resolving external references during bundling.\n * Loader plugins are responsible for handling specific types of external references,\n * such as files, URLs, or custom protocols. Each loader plugin must provide:\n *\n * - A `validate` function to determine if the plugin can handle a given reference string.\n * - An `exec` function to asynchronously fetch and resolve the referenced data,\n * returning a Promise that resolves to a `ResolveResult`.\n *\n * Loader plugins enable extensible support for different reference sources in the bundler.\n *\n * @property type - The plugin type, always 'loader' for loader plugins.\n * @property validate - Function to check if the plugin can handle a given reference value.\n * @property exec - Function to fetch and resolve the reference, returning the resolved data.\n */\nexport type LoaderPlugin = {\n type: 'loader'\n // Returns true if this plugin can handle the given reference value\n validate: (value: string) => boolean\n // Asynchronously fetches and resolves the reference, returning the resolved data\n exec: (value: string) => Promise<ResolveResult>\n}\n\n/**\n * Context information for a node during traversal or processing.\n *\n * Note: The `path` parameter represents the path to the current node being processed.\n * If you are performing a partial bundle (i.e., providing a custom root), this path will be relative\n * to the root you provide, not the absolute root of the original document. You may need to prefix\n * it with your own base path if you want to construct a full path from the absolute document root.\n *\n * - `path`: The JSON pointer path (as an array of strings) from the document root to the current node.\n * - `resolutionCache`: A cache for storing promises of resolved references.\n */\ntype NodeProcessContext = {\n path: readonly string[]\n resolutionCache: Map<string, Promise<Readonly<ResolveResult>>>\n parentNode: UnknownObject | null\n rootNode: UnknownObject\n loaders: LoaderPlugin[]\n}\n\n/**\n * A plugin type for lifecycle hooks, allowing custom logic to be injected into the bundler's process.\n * This type extends the Config['hooks'] interface and is identified by type: 'lifecycle'.\n */\nexport type LifecyclePlugin = { type: 'lifecycle' } & Config['hooks']\n\n/**\n * Represents a plugin used by the bundler for extensibility.\n *\n * Plugins can be either:\n * - Loader plugins: Responsible for resolving and loading external references (e.g., from files, URLs, or custom sources).\n * - Lifecycle plugins: Provide lifecycle hooks to customize or extend the bundling process.\n *\n * Loader plugins must implement:\n * - `validate`: Checks if the plugin can handle a given reference value.\n * - `exec`: Asynchronously resolves and returns the referenced data.\n *\n * Lifecycle plugins extend the bundler's lifecycle hooks for custom logic.\n */\nexport type Plugin = LoaderPlugin | LifecyclePlugin\n\n/**\n * Configuration options for the bundler.\n * Controls how external references are resolved and processed during bundling.\n */\ntype Config = {\n /**\n * Array of plugins that handle resolving references from different sources.\n * Each plugin is responsible for fetching and processing data from specific sources\n * like URLs or the filesystem.\n */\n plugins: Plugin[]\n\n /**\n * Optional root object that serves as the base document when bundling a subpart.\n * This allows resolving references relative to the root document's location,\n * ensuring proper path resolution for nested references.\n */\n root?: UnknownObject\n\n /**\n * Optional maximum depth for reference resolution.\n * Limits how deeply the bundler will follow and resolve nested $ref pointers.\n * Useful for preventing infinite recursion or excessive resource usage.\n */\n depth?: number\n\n /**\n * Optional origin path for the bundler.\n * Used to resolve relative paths in references, especially when the input is a string URL or file path.\n * If not provided, the bundler will use the input value as the origin.\n */\n origin?: string\n\n /**\n * Optional cache to store promises of resolved references.\n * Helps avoid duplicate fetches/reads of the same resource by storing\n * the resolution promises for reuse.\n */\n cache?: Map<string, Promise<ResolveResult>>\n\n /**\n * Cache of visited nodes during partial bundling.\n * Used to prevent re-bundling the same tree multiple times when doing partial bundling,\n * improving performance by avoiding redundant processing of already bundled sections.\n */\n visitedNodes?: Set<unknown>\n\n /**\n * Enable tree shaking to optimize the bundle size.\n * When enabled, only the parts of external documents that are actually referenced\n * will be included in the final bundle.\n */\n treeShake: boolean\n\n /**\n * Optional flag to generate a URL map.\n * When enabled, tracks the original source URLs of bundled references\n * in an x-ext-urls section for reference mapping.\n */\n urlMap?: boolean\n\n /**\n * Optional function to compress input URLs or file paths before bundling.\n * Returns either a Promise resolving to the compressed string or the compressed string directly.\n */\n compress?: (value: string) => Promise<string> | string\n\n /**\n * Optional hooks to monitor the bundler's lifecycle.\n * Allows tracking the progress and status of reference resolution.\n */\n hooks?: Partial<{\n /**\n * Optional hook called when the bundler starts resolving a $ref.\n * Useful for tracking or logging the beginning of a reference resolution.\n */\n onResolveStart: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler fails to resolve a $ref.\n * Can be used for error handling, logging, or custom error reporting.\n */\n onResolveError: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook called when the bundler successfully resolves a $ref.\n * Useful for tracking successful resolutions or custom post-processing.\n */\n onResolveSuccess: (node: UnknownObject & Record<'$ref', unknown>) => void\n /**\n * Optional hook invoked before processing a node.\n * Can be used for preprocessing, mutation, or custom logic before the node is handled by the bundler.\n */\n onBeforeNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n /**\n * Optional hook invoked after processing a node.\n * Useful for postprocessing, cleanup, or custom logic after the node has been handled by the bundler.\n */\n onAfterNodeProcess: (node: UnknownObject, context: NodeProcessContext) => void | Promise<void>\n }>\n}\n\n/**\n * Extension keys used for bundling external references in OpenAPI documents.\n * These custom extensions help maintain the structure and traceability of bundled documents.\n */\nconst extensions = {\n /**\n * Custom OpenAPI extension key used to store external references.\n * This key will contain all bundled external documents.\n * The x-ext key is used to maintain a clean separation between the main\n * OpenAPI document and its bundled external references.\n */\n externalDocuments: 'x-ext',\n\n /**\n * Custom OpenAPI extension key used to maintain a mapping between\n * hashed keys and their original URLs in x-ext.\n * This mapping is essential for tracking the source of bundled references\n */\n externalDocumentsMappings: 'x-ext-urls',\n} as const\n\n/**\n * Bundles an OpenAPI specification by resolving all external references.\n * This function traverses the input object recursively and embeds external $ref\n * references into an x-ext section. External references can be URLs or local files.\n * The original $refs are updated to point to their embedded content in the x-ext section.\n * If the input is an object, it will be modified in place by adding an x-ext\n * property to store resolved external references.\n *\n * @param input - The OpenAPI specification to bundle. Can be either an object or string.\n * If a string is provided, it will be resolved using the provided plugins.\n * If no plugin can process the input, the onReferenceError hook will be invoked\n * and an error will be emitted to the console.\n * @param config - Configuration object containing plugins and options for bundling OpenAPI specifications\n * @returns A promise that resolves to the bundled specification with all references embedded\n * @example\n * // Example with object input\n * const spec = {\n * paths: {\n * '/users': {\n * $ref: 'https://example.com/schemas/users.yaml'\n * }\n * }\n * }\n *\n * const bundled = await bundle(spec, {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // Result:\n * // {\n * // paths: {\n * // '/users': {\n * // $ref: '#/x-ext/abc123'\n * // }\n * // },\n * // 'x-ext': {\n * // 'abc123': {\n * // // Resolved content from users.yaml\n * // }\n * // },\n * // 'x-ext-urls': {\n * // 'https://example.com/schemas/users.yaml': 'abc123'\n * // }\n * // }\n *\n * // Example with URL input\n * const bundledFromUrl = await bundle('https://example.com/openapi.yaml', {\n * plugins: [fetchUrls()],\n * treeShake: true,\n * urlMap: true,\n * hooks: {\n * onResolveStart: (ref) => console.log('Resolving:', ref.$ref),\n * onResolveSuccess: (ref) => console.log('Resolved:', ref.$ref),\n * onResolveError: (ref) => console.log('Failed to resolve:', ref.$ref)\n * }\n * })\n * // The function will first fetch the OpenAPI spec from the URL,\n * // then bundle all its external references into the x-ext section\n */\nexport async function bundle(input: UnknownObject | string, config: Config) {\n // Cache for storing promises of resolved external references (URLs and local files)\n // to avoid duplicate fetches/reads of the same resource\n const cache = config.cache ?? new Map<string, Promise<ResolveResult>>()\n\n const loaderPlugins = config.plugins.filter((it) => it.type === 'loader')\n const lifecyclePlugin = config.plugins.filter((it) => it.type === 'lifecycle')\n\n /**\n * Resolves the input value by either returning it directly if it's not a string,\n * or attempting to resolve it using the provided plugins if it is a string.\n * @returns The resolved input data or throws an error if resolution fails\n */\n const resolveInput = async () => {\n if (typeof input !== 'string') {\n return input\n }\n const result = await resolveContents(input, loaderPlugins)\n\n if (result.ok && typeof result.data === 'object') {\n return result.data\n }\n\n throw new Error(\n 'Failed to resolve input: Please provide a valid string value or pass a loader to process the input',\n )\n }\n\n // Resolve the input specification, which could be either a direct object or a string URL/path\n const rawSpecification = await resolveInput()\n\n // Document root used to write all external documents\n // We need this when we want to do a partial bundle of a document\n const documentRoot = config.root ?? rawSpecification\n\n // Determines if the bundling operation is partial.\n // Partial bundling occurs when:\n // - A root document is provided that is different from the raw specification being bundled, or\n // - A maximum depth is specified in the config.\n // In these cases, only a subset of the document may be bundled.\n const isPartialBundling =\n (config.root !== undefined && config.root !== rawSpecification) || config.depth !== undefined\n\n // Set of nodes that have already been processed during bundling to prevent duplicate processing\n const processedNodes = config.visitedNodes ?? new Set()\n\n // Determines the initial origin path for the bundler based on the input type.\n // For string inputs that are URLs or file paths, uses the input as the origin.\n // For non-string inputs or other string types, returns an empty string.\n const defaultOrigin = () => {\n if (config.origin) {\n return config.origin\n }\n\n if (typeof input !== 'string') {\n return ''\n }\n\n if (isRemoteUrl(input) || isFilePath(input)) {\n return input\n }\n\n return ''\n }\n\n // Create the cache to store the compressed values to their map values\n if (documentRoot[extensions.externalDocumentsMappings] === undefined) {\n documentRoot[extensions.externalDocumentsMappings] = {}\n }\n const { generate } = uniqueValueGeneratorFactory(\n config.compress ?? getHash,\n documentRoot[extensions.externalDocumentsMappings],\n )\n\n const bundler = async (\n root: unknown,\n origin: string = defaultOrigin(),\n isChunkParent = false,\n depth = 0,\n path: readonly string[] = [],\n parent: UnknownObject = null,\n ) => {\n // If a maximum depth is set in the config, stop bundling when the current depth reaches or exceeds it\n if (config.depth !== undefined && depth > config.depth) {\n return\n }\n\n if (!isObject(root) && !Array.isArray(root)) {\n return\n }\n\n // Skip if this node has already been processed to prevent infinite recursion\n // and duplicate processing of the same node\n if (processedNodes.has(root)) {\n return\n }\n // Mark this node as processed before continuing\n processedNodes.add(root)\n\n // Invoke the onBeforeNodeProcess hook for the current node before any further processing\n await config.hooks?.onBeforeNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n // Invoke onBeforeNodeProcess hooks from all registered lifecycle plugins\n for (const plugin of lifecyclePlugin) {\n await plugin.onBeforeNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n }\n\n if (typeof root === 'object' && '$ref' in root && typeof root['$ref'] === 'string') {\n const ref = root['$ref']\n const isChunk = '$global' in root && typeof root['$global'] === 'boolean' && root['$global']\n\n if (isLocalRef(ref)) {\n if (isPartialBundling) {\n const segments = getSegmentsFromPath(ref.substring(1))\n const parent = segments.length > 0 ? getNestedValue(documentRoot, segments.slice(0, -1)) : undefined\n\n // When doing partial bundling, we need to recursively bundle all dependencies\n // referenced by this local reference to ensure the partial bundle is complete.\n // This includes not just the direct reference but also all its dependencies,\n // creating a complete and self-contained partial bundle.\n await bundler(getNestedValue(documentRoot, segments), origin, isChunkParent, depth + 1, segments, parent)\n }\n return\n }\n\n const [prefix, path = ''] = ref.split('#', 2)\n\n // Combine the current origin with the new path to resolve relative references\n // correctly within the context of the external file being processed\n const resolvedPath = resolveReferencePath(origin, prefix)\n\n // Generate a unique compressed path for the external document\n // This is used as a key to store and reference the bundled external document\n // The compression helps reduce the overall file size of the bundled document\n const compressedPath = await generate(resolvedPath)\n\n const seen = cache.has(resolvedPath)\n\n if (!seen) {\n cache.set(resolvedPath, resolveContents(resolvedPath, loaderPlugins))\n }\n\n config?.hooks?.onResolveStart?.(root)\n lifecyclePlugin.forEach((it) => it.onResolveStart?.(root))\n\n // Resolve the remote document\n const result = await cache.get(resolvedPath)\n\n if (result.ok) {\n // Process the result only once to avoid duplicate processing and prevent multiple prefixing\n // of internal references, which would corrupt the reference paths\n if (!seen) {\n // Skip prefixing for chunks since they are meant to be self-contained and their\n // internal references should remain relative to their original location. Chunks\n // are typically used for modular components that need to maintain their own\n // reference context without being affected by the main document's structure.\n if (!isChunk) {\n // Update internal references in the resolved document to use the correct base path.\n // When we embed external documents, their internal references need to be updated to\n // maintain the correct path context relative to the main document. This is crucial\n // because internal references in the external document are relative to its original\n // location, but when embedded, they need to be relative to their new location in\n // the main document's x-ext section. Without this update, internal references\n // would point to incorrect locations and break the document structure.\n prefixInternalRefRecursive(result.data, [extensions.externalDocuments, compressedPath])\n }\n\n // Recursively process the resolved content\n // to handle any nested references it may contain. We pass the resolvedPath as the new origin\n // to ensure any relative references within this content are resolved correctly relative to\n // their new location in the bundled document.\n await bundler(result.data, isChunk ? origin : resolvedPath, isChunk, depth + 1, [\n extensions.externalDocuments,\n compressedPath,\n documentRoot[extensions.externalDocumentsMappings],\n ])\n\n // Store the mapping between hashed keys and original URLs in x-ext-urls\n // This allows tracking which external URLs were bundled and their corresponding locations\n setValueAtPath(\n documentRoot,\n `/${extensions.externalDocumentsMappings}/${escapeJsonPointer(compressedPath)}`,\n resolvedPath,\n )\n }\n\n if (config.treeShake === true) {\n // Store only the subtree that is actually used\n // This optimizes the bundle size by only including the parts of the external document\n // that are referenced, rather than the entire document\n resolveAndCopyReferences(\n documentRoot,\n { [extensions.externalDocuments]: { [compressedPath]: result.data } },\n prefixInternalRef(`#${path}`, [extensions.externalDocuments, compressedPath]).substring(1),\n extensions.externalDocuments,\n compressedPath,\n )\n } else if (!seen) {\n // Store the external document in the main document's x-ext key\n // When tree shaking is disabled, we include the entire external document\n // This preserves all content and is faster since we don't need to analyze and copy\n // specific parts. This approach is ideal when storing the result in memory\n // as it avoids the overhead of tree shaking operations\n setValueAtPath(documentRoot, `/${extensions.externalDocuments}/${compressedPath}`, result.data)\n }\n\n // Update the $ref to point to the embedded document in x-ext\n // This is necessary because we need to maintain the correct path context\n // for the embedded document while preserving its internal structure\n root.$ref = prefixInternalRef(`#${path}`, [extensions.externalDocuments, compressedPath])\n\n config?.hooks?.onResolveSuccess?.(root)\n lifecyclePlugin.forEach((it) => it.onResolveSuccess?.(root))\n\n return\n }\n\n config?.hooks?.onResolveError?.(root)\n lifecyclePlugin.forEach((it) => it.onResolveError?.(root))\n\n return console.warn(\n `Failed to resolve external reference \"${resolvedPath}\". The reference may be invalid, inaccessible, or missing a loader for this type of reference.`,\n )\n }\n\n // Recursively traverse all child properties of the current object to resolve nested $ref references.\n // This step ensures that any $refs located deeper within the object hierarchy are discovered and processed.\n // We explicitly skip the extension keys (x-ext and x-ext-urls) to avoid reprocessing already bundled or mapped content.\n await Promise.all(\n Object.entries(root).map(async ([key, value]) => {\n if (key === extensions.externalDocuments || key === extensions.externalDocumentsMappings) {\n return\n }\n\n await bundler(value, origin, isChunkParent, depth + 1, [...path, key], root as UnknownObject)\n }),\n )\n\n // Invoke the optional onAfterNodeProcess hook from the config, if provided.\n // This allows for custom post-processing logic after a node has been handled by the bundler.\n await config.hooks?.onAfterNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n\n // Iterate through all lifecycle plugins and invoke their onAfterNodeProcess hooks, if defined.\n // This enables plugins to perform additional post-processing or cleanup after the node is processed.\n for (const plugin of lifecyclePlugin) {\n await plugin.onAfterNodeProcess?.(root as UnknownObject, {\n path,\n resolutionCache: cache,\n parentNode: parent,\n rootNode: documentRoot as UnknownObject,\n loaders: loaderPlugins,\n })\n }\n }\n\n await bundler(rawSpecification)\n\n // Keep urlMappings when doing partial bundling to track hash values and handle collisions\n // For full bundling without urlMap config, remove the mappings to clean up the output\n if (!config.urlMap && !isPartialBundling) {\n // Remove the external document mappings from the output when doing a full bundle without urlMap config\n delete documentRoot[extensions.externalDocumentsMappings]\n }\n\n return rawSpecification\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "parent"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"value-generator.d.ts","sourceRoot":"","sources":["../../src/bundle/value-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,mBAe1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,mBAAmB,CAAC,EAAE,MAAM,EAC5B,KAAK,SAAI,mBAoBV;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,2BAA2B,aAC5B,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,qBAClC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAMvC;;;;;;;;;;;;OAYG;sBACqB,MAAM;CAqBjC,CAAA"}
1
+ {"version":3,"file":"value-generator.d.ts","sourceRoot":"","sources":["../../src/bundle/value-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,mBAe1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,mBAAmB,CAAC,EAAE,MAAM,EAC5B,KAAK,SAAI,mBAoBV;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAU,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,mBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAMvC;;;;;;;;;;;;OAYG;sBACqB,MAAM;CAqBjC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,SAClD,aAAa,YACV,IAAI,KACb,uBAAuB,CAAC,IAAI,CAgC9B,CAAA"}
1
+ {"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EACzD,OAAO,aAAa,EACpB,UAAU,IAAI,KACb,uBAAuB,CAAC,IAAI,CAgC9B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/diff/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAC3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAC3B,UAAU,CAAC,CAAC,CAAC,EAAE,KACpB,CAyCF,CAAA"}
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/diff/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrD,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE,KACpB,CAyCF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,EAAE,IAAI;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,oBA0C7F,CAAA"}
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,EAAE,IAAI;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,oBA0C7F,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/diff/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE;;;CA8FtE,CAAA"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/diff/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE;;;CA8FtE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/diff/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,MAAO,OAAO,KAAK,OAAO,YAsBrD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,MAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAe3G,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,EAAE,YAY7C,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/diff/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,YAsBrD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAe3G,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,YAY7C,CAAA"}
@@ -7,6 +7,8 @@ import type { UnknownObject } from '../types.js';
7
7
  * will resolve and return the referenced value from the root object.
8
8
  * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution
9
9
  * works at any depth.
10
+ * - Properties starting with an underscore (_) are hidden and will not be accessible through
11
+ * the proxy (returns undefined on access, false on 'in' checks, excluded from enumeration).
10
12
  * - Setting, deleting, and enumerating properties works as expected, including for proxied references.
11
13
  *
12
14
  * @param target - The object or array to wrap in a magic proxy
@@ -18,18 +20,26 @@ import type { UnknownObject } from '../types.js';
18
20
  * definitions: {
19
21
  * foo: { bar: 123 }
20
22
  * },
21
- * refObj: { $ref: '#/definitions/foo' }
23
+ * refObj: { $ref: '#/definitions/foo' },
24
+ * _internal: 'hidden property'
22
25
  * }
23
26
  * const proxy = createMagicProxy(input)
24
27
  *
25
28
  * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }
26
29
  * console.log(proxy.refObj['$ref-value']) // { bar: 123 }
27
30
  *
31
+ * // Properties starting with underscore are hidden
32
+ * console.log(proxy._internal) // undefined
33
+ * console.log('_internal' in proxy) // false
34
+ * console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')
35
+ *
28
36
  * // Setting and deleting properties works as expected
29
37
  * proxy.refObj.extra = 'hello'
30
38
  * delete proxy.refObj.extra
31
39
  */
32
- export declare const createMagicProxy: <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(target: T, root?: S | T, cache?: Map<string, unknown>) => T;
40
+ export declare const createMagicProxy: <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(target: T, options?: {
41
+ showInternal?: boolean;
42
+ }, root?: S | T, cache?: Map<string, unknown>, proxyCache?: WeakMap<object, T>) => T;
33
43
  /**
34
44
  * Gets the raw (non-proxied) version of an object created by createMagicProxy.
35
45
  * This is useful when you need to access the original object without the magic proxy wrapper.
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,aAAa,UAC3F,CAAC,SACH,CAAC,GAAG,CAAC,oCAyIZ,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAUnC"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,aAAa,EACnG,QAAQ,CAAC,EACT,UAAU;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,EACpC,OAAM,CAAC,GAAG,CAAU,EACpB,4BAAkC,EAClC,+BAAqC,MAgLtC,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAUnC"}
@@ -6,15 +6,19 @@ const isMagicProxy = Symbol("isMagicProxy");
6
6
  const magicProxyTarget = Symbol("magicProxyTarget");
7
7
  const REF_VALUE = "$ref-value";
8
8
  const REF_KEY = "$ref";
9
- const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map()) => {
9
+ const createMagicProxy = (target, options, root = target, cache = /* @__PURE__ */ new Map(), proxyCache = /* @__PURE__ */ new WeakMap()) => {
10
10
  if (!isObject(target) && !Array.isArray(target)) {
11
11
  return target;
12
12
  }
13
+ if (proxyCache.has(target)) {
14
+ return proxyCache.get(target);
15
+ }
13
16
  const handler = {
14
17
  /**
15
18
  * Proxy "get" trap for magic proxy.
16
19
  * - If accessing the special isMagicProxy symbol, return true to identify proxy.
17
20
  * - If accessing the magicProxyTarget symbol, return the original target object.
21
+ * - Hide properties starting with underscore by returning undefined.
18
22
  * - If accessing "$ref-value" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.
19
23
  * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).
20
24
  */
@@ -26,25 +30,34 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
26
30
  return target2;
27
31
  }
28
32
  const ref = Reflect.get(target2, REF_KEY, receiver);
33
+ if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
34
+ return void 0;
35
+ }
29
36
  if (prop === REF_VALUE && typeof ref === "string" && isLocalRef(ref)) {
30
37
  if (cache.has(ref)) {
31
38
  return cache.get(ref);
32
39
  }
33
40
  const resolvedValue = getValueByPath(root, parseJsonPointer(ref));
34
- const proxiedValue = createMagicProxy(resolvedValue, root, cache);
41
+ const proxiedValue = createMagicProxy(resolvedValue, options, root, cache);
35
42
  cache.set(ref, proxiedValue);
36
43
  return proxiedValue;
37
44
  }
38
45
  const value = Reflect.get(target2, prop, receiver);
39
- return createMagicProxy(value, root, cache);
46
+ return createMagicProxy(value, options, root, cache, proxyCache);
40
47
  },
41
48
  /**
42
49
  * Proxy "set" trap for magic proxy.
43
50
  * Allows setting properties on the proxied object.
44
51
  * This will update the underlying target object.
52
+ *
53
+ * Note: it will not update if the property starts with an underscore (_)
54
+ * Those will be considered private properties by the proxy
45
55
  */
46
56
  set(target2, prop, newValue, receiver) {
47
57
  const ref = Reflect.get(target2, REF_KEY, receiver);
58
+ if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
59
+ return true;
60
+ }
48
61
  if (prop === REF_VALUE && typeof ref === "string" && isLocalRef(ref)) {
49
62
  const segments = getSegmentsFromPath(ref);
50
63
  if (segments.length === 0) {
@@ -72,9 +85,13 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
72
85
  * - Pretend that "$ref-value" exists if "$ref" exists on the target.
73
86
  * This allows expressions like `"$ref-value" in obj` to return true for objects with a $ref,
74
87
  * even though "$ref-value" is a virtual property provided by the proxy.
88
+ * - Hide properties starting with underscore by returning false.
75
89
  * - For all other properties, defer to the default Reflect.has behavior.
76
90
  */
77
91
  has(target2, prop) {
92
+ if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
93
+ return false;
94
+ }
78
95
  if (prop === REF_VALUE && REF_KEY in target2) {
79
96
  return true;
80
97
  }
@@ -86,21 +103,29 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
86
103
  * - If the object has a "$ref" property, ensures that "$ref-value" is also included in the keys,
87
104
  * even though "$ref-value" is a virtual property provided by the proxy.
88
105
  * This allows Object.keys, Reflect.ownKeys, etc. to include "$ref-value" for objects with $ref.
106
+ * - Filters out properties starting with underscore.
89
107
  */
90
108
  ownKeys(target2) {
91
109
  const keys = Reflect.ownKeys(target2);
92
- if (REF_KEY in target2 && !keys.includes(REF_VALUE)) {
93
- keys.push(REF_VALUE);
110
+ const filteredKeys = keys.filter(
111
+ (key) => typeof key !== "string" || !(key.startsWith("_") && !options?.showInternal)
112
+ );
113
+ if (REF_KEY in target2 && !filteredKeys.includes(REF_VALUE)) {
114
+ filteredKeys.push(REF_VALUE);
94
115
  }
95
- return keys;
116
+ return filteredKeys;
96
117
  },
97
118
  /**
98
119
  * Proxy "getOwnPropertyDescriptor" trap for magic proxy.
99
120
  * - For the virtual "$ref-value" property, returns a descriptor that makes it appear as a regular property.
121
+ * - Hide properties starting with underscore by returning undefined.
100
122
  * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.
101
123
  * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.
102
124
  */
103
125
  getOwnPropertyDescriptor(target2, prop) {
126
+ if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
127
+ return void 0;
128
+ }
104
129
  const ref = Reflect.get(target2, REF_KEY);
105
130
  if (prop === REF_VALUE && typeof ref === "string") {
106
131
  return {
@@ -113,7 +138,9 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
113
138
  return Reflect.getOwnPropertyDescriptor(target2, prop);
114
139
  }
115
140
  };
116
- return new Proxy(target, handler);
141
+ const proxied = new Proxy(target, handler);
142
+ proxyCache.set(target, proxied);
143
+ return proxied;
117
144
  };
118
145
  function getRaw(obj) {
119
146
  if (typeof obj !== "object" || obj === null) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/magic-proxy/proxy.ts"],
4
- "sourcesContent": ["import { isLocalRef } from '@/bundle/bundle'\nimport type { UnknownObject } from '@/types'\nimport { getSegmentsFromPath } from '@/utils/get-segments-from-path'\nimport { isObject } from '@/utils/is-object'\nimport { getValueByPath, parseJsonPointer } from '@/utils/json-path-utils'\n\nconst isMagicProxy = Symbol('isMagicProxy')\nconst magicProxyTarget = Symbol('magicProxyTarget')\n\nconst REF_VALUE = '$ref-value'\nconst REF_KEY = '$ref'\n\n/**\n * Creates a \"magic\" proxy for a given object or array, enabling transparent access to\n * JSON Reference ($ref) values as if they were directly present on the object.\n *\n * - If an object contains a `$ref` property, accessing the special `$ref-value` property\n * will resolve and return the referenced value from the root object.\n * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution\n * works at any depth.\n * - Setting, deleting, and enumerating properties works as expected, including for proxied references.\n *\n * @param target - The object or array to wrap in a magic proxy\n * @param root - The root object for resolving local JSON references (defaults to target)\n * @returns A proxied version of the input object/array with magic $ref-value support\n *\n * @example\n * const input = {\n * definitions: {\n * foo: { bar: 123 }\n * },\n * refObj: { $ref: '#/definitions/foo' }\n * }\n * const proxy = createMagicProxy(input)\n *\n * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }\n * console.log(proxy.refObj['$ref-value']) // { bar: 123 }\n *\n * // Setting and deleting properties works as expected\n * proxy.refObj.extra = 'hello'\n * delete proxy.refObj.extra\n */\nexport const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(\n target: T,\n root: S | T = target,\n cache = new Map<string, unknown>(),\n) => {\n if (!isObject(target) && !Array.isArray(target)) {\n return target\n }\n\n const handler: ProxyHandler<T> = {\n /**\n * Proxy \"get\" trap for magic proxy.\n * - If accessing the special isMagicProxy symbol, return true to identify proxy.\n * - If accessing the magicProxyTarget symbol, return the original target object.\n * - If accessing \"$ref-value\" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.\n * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).\n */\n get(target, prop, receiver) {\n if (prop === isMagicProxy) {\n // Used to identify if an object is a magic proxy\n return true\n }\n\n if (prop === magicProxyTarget) {\n // Used to retrieve the original target object from the proxy\n return target\n }\n\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n // If accessing \"$ref-value\" and $ref is a local reference, resolve and return the referenced value\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n // Check cache first for performance optimization\n if (cache.has(ref)) {\n return cache.get(ref)\n }\n\n // Resolve the reference and create a new magic proxy\n const resolvedValue = getValueByPath(root, parseJsonPointer(ref))\n const proxiedValue = createMagicProxy(resolvedValue, root, cache)\n\n // Store in cache for future lookups\n cache.set(ref, proxiedValue)\n return proxiedValue\n }\n\n // For all other properties, recursively wrap the value in a magic proxy\n const value = Reflect.get(target, prop, receiver)\n return createMagicProxy(value, root, cache)\n },\n /**\n * Proxy \"set\" trap for magic proxy.\n * Allows setting properties on the proxied object.\n * This will update the underlying target object.\n */\n set(target, prop, newValue, receiver) {\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n const segments = getSegmentsFromPath(ref)\n\n if (segments.length === 0) {\n return false // Can not set top level $ref-value\n }\n\n const parentNode = getValueByPath(root, segments.slice(0, -1))\n\n // TODO: Maybe we create the path if it does not exist?\n // TODO: This can allow for invalid references to not throw errors\n if (!parentNode || (!isObject(parentNode) && !Array.isArray(parentNode))) {\n return false // Parent node does not exist, cannot set $ref-value\n }\n parentNode[segments.at(-1)] = newValue\n return true\n }\n\n return Reflect.set(target, prop, newValue, receiver)\n },\n /**\n * Proxy \"deleteProperty\" trap for magic proxy.\n * Allows deleting properties from the proxied object.\n * This will update the underlying target object.\n */\n deleteProperty(target, prop) {\n return Reflect.deleteProperty(target, prop)\n },\n /**\n * Proxy \"has\" trap for magic proxy.\n * - Pretend that \"$ref-value\" exists if \"$ref\" exists on the target.\n * This allows expressions like `\"$ref-value\" in obj` to return true for objects with a $ref,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * - For all other properties, defer to the default Reflect.has behavior.\n */\n has(target, prop) {\n // Pretend that \"$ref-value\" exists if \"$ref\" exists\n if (prop === REF_VALUE && REF_KEY in target) {\n return true\n }\n return Reflect.has(target, prop)\n },\n /**\n * Proxy \"ownKeys\" trap for magic proxy.\n * - Returns the list of own property keys for the proxied object.\n * - If the object has a \"$ref\" property, ensures that \"$ref-value\" is also included in the keys,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * This allows Object.keys, Reflect.ownKeys, etc. to include \"$ref-value\" for objects with $ref.\n */\n ownKeys(target) {\n const keys = Reflect.ownKeys(target)\n if (REF_KEY in target && !keys.includes(REF_VALUE)) {\n keys.push(REF_VALUE)\n }\n return keys\n },\n\n /**\n * Proxy \"getOwnPropertyDescriptor\" trap for magic proxy.\n * - For the virtual \"$ref-value\" property, returns a descriptor that makes it appear as a regular property.\n * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.\n * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.\n */\n getOwnPropertyDescriptor(target, prop) {\n const ref = Reflect.get(target, REF_KEY)\n\n if (prop === REF_VALUE && typeof ref === 'string') {\n return {\n configurable: true,\n enumerable: true,\n value: undefined,\n writable: false,\n }\n }\n\n // Otherwise, delegate to the default behavior\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n }\n\n return new Proxy<T>(target, handler)\n}\n\n/**\n * Gets the raw (non-proxied) version of an object created by createMagicProxy.\n * This is useful when you need to access the original object without the magic proxy wrapper.\n *\n * @param obj - The magic proxy object to get the raw version of\n * @returns The raw version of the object\n * @example\n * const proxy = createMagicProxy({ foo: { $ref: '#/bar' } })\n * const raw = getRaw(proxy) // { foo: { $ref: '#/bar' } }\n */\nexport function getRaw<T>(obj: T): T {\n if (typeof obj !== 'object' || obj === null) {\n return obj\n }\n\n if ((obj as T & { [isMagicProxy]: boolean | undefined })[isMagicProxy]) {\n return (obj as T & { [magicProxyTarget]: T })[magicProxyTarget]\n }\n\n return obj\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { isLocalRef } from '@/bundle/bundle'\nimport type { UnknownObject } from '@/types'\nimport { getSegmentsFromPath } from '@/utils/get-segments-from-path'\nimport { isObject } from '@/utils/is-object'\nimport { getValueByPath, parseJsonPointer } from '@/utils/json-path-utils'\n\nconst isMagicProxy = Symbol('isMagicProxy')\nconst magicProxyTarget = Symbol('magicProxyTarget')\n\nconst REF_VALUE = '$ref-value'\nconst REF_KEY = '$ref'\n\n/**\n * Creates a \"magic\" proxy for a given object or array, enabling transparent access to\n * JSON Reference ($ref) values as if they were directly present on the object.\n *\n * - If an object contains a `$ref` property, accessing the special `$ref-value` property\n * will resolve and return the referenced value from the root object.\n * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution\n * works at any depth.\n * - Properties starting with an underscore (_) are hidden and will not be accessible through\n * the proxy (returns undefined on access, false on 'in' checks, excluded from enumeration).\n * - Setting, deleting, and enumerating properties works as expected, including for proxied references.\n *\n * @param target - The object or array to wrap in a magic proxy\n * @param root - The root object for resolving local JSON references (defaults to target)\n * @returns A proxied version of the input object/array with magic $ref-value support\n *\n * @example\n * const input = {\n * definitions: {\n * foo: { bar: 123 }\n * },\n * refObj: { $ref: '#/definitions/foo' },\n * _internal: 'hidden property'\n * }\n * const proxy = createMagicProxy(input)\n *\n * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }\n * console.log(proxy.refObj['$ref-value']) // { bar: 123 }\n *\n * // Properties starting with underscore are hidden\n * console.log(proxy._internal) // undefined\n * console.log('_internal' in proxy) // false\n * console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')\n *\n * // Setting and deleting properties works as expected\n * proxy.refObj.extra = 'hello'\n * delete proxy.refObj.extra\n */\nexport const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(\n target: T,\n options?: { showInternal?: boolean },\n root: S | T = target,\n cache = new Map<string, unknown>(),\n proxyCache = new WeakMap<object, T>(),\n) => {\n if (!isObject(target) && !Array.isArray(target)) {\n return target\n }\n\n // Return existing proxy for the same target to ensure referential stability\n if (proxyCache.has(target)) {\n return proxyCache.get(target)\n }\n\n const handler: ProxyHandler<T> = {\n /**\n * Proxy \"get\" trap for magic proxy.\n * - If accessing the special isMagicProxy symbol, return true to identify proxy.\n * - If accessing the magicProxyTarget symbol, return the original target object.\n * - Hide properties starting with underscore by returning undefined.\n * - If accessing \"$ref-value\" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.\n * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).\n */\n get(target, prop, receiver) {\n if (prop === isMagicProxy) {\n // Used to identify if an object is a magic proxy\n return true\n }\n\n if (prop === magicProxyTarget) {\n // Used to retrieve the original target object from the proxy\n return target\n }\n\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n // Hide properties starting with underscore - these are considered internal/private properties\n // and should not be accessible through the magic proxy interface\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return undefined\n }\n\n // If accessing \"$ref-value\" and $ref is a local reference, resolve and return the referenced value\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n // Check cache first for performance optimization\n if (cache.has(ref)) {\n return cache.get(ref)\n }\n\n // Resolve the reference and create a new magic proxy\n const resolvedValue = getValueByPath(root, parseJsonPointer(ref))\n const proxiedValue = createMagicProxy(resolvedValue, options, root, cache)\n\n // Store in cache for future lookups\n cache.set(ref, proxiedValue)\n return proxiedValue\n }\n\n // For all other properties, recursively wrap the value in a magic proxy\n const value = Reflect.get(target, prop, receiver)\n return createMagicProxy(value as T, options, root, cache, proxyCache)\n },\n /**\n * Proxy \"set\" trap for magic proxy.\n * Allows setting properties on the proxied object.\n * This will update the underlying target object.\n *\n * Note: it will not update if the property starts with an underscore (_)\n * Those will be considered private properties by the proxy\n */\n set(target, prop, newValue, receiver) {\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return true\n }\n\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n const segments = getSegmentsFromPath(ref)\n\n if (segments.length === 0) {\n return false // Can not set top level $ref-value\n }\n\n const parentNode = getValueByPath(root, segments.slice(0, -1))\n\n // TODO: Maybe we create the path if it does not exist?\n // TODO: This can allow for invalid references to not throw errors\n if (!parentNode || (!isObject(parentNode) && !Array.isArray(parentNode))) {\n return false // Parent node does not exist, cannot set $ref-value\n }\n parentNode[segments.at(-1)] = newValue\n return true\n }\n\n return Reflect.set(target, prop, newValue, receiver)\n },\n /**\n * Proxy \"deleteProperty\" trap for magic proxy.\n * Allows deleting properties from the proxied object.\n * This will update the underlying target object.\n */\n deleteProperty(target, prop) {\n return Reflect.deleteProperty(target, prop)\n },\n /**\n * Proxy \"has\" trap for magic proxy.\n * - Pretend that \"$ref-value\" exists if \"$ref\" exists on the target.\n * This allows expressions like `\"$ref-value\" in obj` to return true for objects with a $ref,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * - Hide properties starting with underscore by returning false.\n * - For all other properties, defer to the default Reflect.has behavior.\n */\n has(target, prop) {\n // Hide properties starting with underscore\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return false\n }\n\n // Pretend that \"$ref-value\" exists if \"$ref\" exists\n if (prop === REF_VALUE && REF_KEY in target) {\n return true\n }\n return Reflect.has(target, prop)\n },\n /**\n * Proxy \"ownKeys\" trap for magic proxy.\n * - Returns the list of own property keys for the proxied object.\n * - If the object has a \"$ref\" property, ensures that \"$ref-value\" is also included in the keys,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * This allows Object.keys, Reflect.ownKeys, etc. to include \"$ref-value\" for objects with $ref.\n * - Filters out properties starting with underscore.\n */\n ownKeys(target) {\n const keys = Reflect.ownKeys(target)\n\n // Filter out properties starting with underscore\n const filteredKeys = keys.filter(\n (key) => typeof key !== 'string' || !(key.startsWith('_') && !options?.showInternal),\n )\n\n if (REF_KEY in target && !filteredKeys.includes(REF_VALUE)) {\n filteredKeys.push(REF_VALUE)\n }\n return filteredKeys\n },\n\n /**\n * Proxy \"getOwnPropertyDescriptor\" trap for magic proxy.\n * - For the virtual \"$ref-value\" property, returns a descriptor that makes it appear as a regular property.\n * - Hide properties starting with underscore by returning undefined.\n * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.\n * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.\n */\n getOwnPropertyDescriptor(target, prop) {\n // Hide properties starting with underscore\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return undefined\n }\n\n const ref = Reflect.get(target, REF_KEY)\n\n if (prop === REF_VALUE && typeof ref === 'string') {\n return {\n configurable: true,\n enumerable: true,\n value: undefined,\n writable: false,\n }\n }\n\n // Otherwise, delegate to the default behavior\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n }\n\n const proxied = new Proxy<T>(target, handler)\n proxyCache.set(target, proxied)\n return proxied\n}\n\n/**\n * Gets the raw (non-proxied) version of an object created by createMagicProxy.\n * This is useful when you need to access the original object without the magic proxy wrapper.\n *\n * @param obj - The magic proxy object to get the raw version of\n * @returns The raw version of the object\n * @example\n * const proxy = createMagicProxy({ foo: { $ref: '#/bar' } })\n * const raw = getRaw(proxy) // { foo: { $ref: '#/bar' } }\n */\nexport function getRaw<T>(obj: T): T {\n if (typeof obj !== 'object' || obj === null) {\n return obj\n }\n\n if ((obj as T & { [isMagicProxy]: boolean | undefined })[isMagicProxy]) {\n return (obj as T & { [magicProxyTarget]: T })[magicProxyTarget]\n }\n\n return obj\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["target"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"is-object.d.ts","sourceRoot":"","sources":["../../src/utils/is-object.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ,QAAS,GAAG,KAAG,GAAG,IAAI,MAAwE,CAAA"}
1
+ {"version":3,"file":"is-object.d.ts","sourceRoot":"","sources":["../../src/utils/is-object.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,MAAwE,CAAA"}
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "url": "git+https://github.com/scalar/scalar.git",
11
11
  "directory": "packages/json-magic"
12
12
  },
13
- "version": "0.3.0",
13
+ "version": "0.4.0",
14
14
  "engines": {
15
15
  "node": ">=20"
16
16
  },
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "vue": "^3.5.17",
52
52
  "yaml": "2.8.0",
53
- "@scalar/helpers": "0.0.8"
53
+ "@scalar/helpers": "0.0.9"
54
54
  },
55
55
  "devDependencies": {
56
56
  "fastify": "^5.3.3",
@@ -1151,7 +1151,7 @@ describe('bundle', () => {
1151
1151
  [await getHash(`${url}/external/document.json`)]: {
1152
1152
  external: 'external',
1153
1153
  someChunk: {
1154
- $ref: `#/x-ext/${await getHash(`${url}/external/chunk3`)}`,
1154
+ $ref: `#/x-ext/${await getHash(`${url}/chunk3`)}`,
1155
1155
  $global: true,
1156
1156
  },
1157
1157
  },
@@ -1167,14 +1167,14 @@ describe('bundle', () => {
1167
1167
  $ref: '#/c',
1168
1168
  },
1169
1169
  },
1170
- [await getHash(`${url}/external/chunk3`)]: {
1170
+ [await getHash(`${url}/chunk3`)]: {
1171
1171
  chunk3: 'chunk3',
1172
1172
  },
1173
1173
  },
1174
1174
  'x-ext-urls': {
1175
1175
  [await getHash(`${url}/chunk1`)]: `${url}/chunk1`,
1176
1176
  [await getHash(`${url}/chunk2`)]: `${url}/chunk2`,
1177
- [await getHash(`${url}/external/chunk3`)]: `${url}/external/chunk3`,
1177
+ [await getHash(`${url}/chunk3`)]: `${url}/chunk3`,
1178
1178
  [await getHash(`${url}/external/document.json`)]: `${url}/external/document.json`,
1179
1179
  },
1180
1180
  })
@@ -1309,7 +1309,7 @@ describe('bundle', () => {
1309
1309
  $ref: '/d#',
1310
1310
  },
1311
1311
  }))
1312
- server.get('/a/b/d', () => ({
1312
+ server.get('/d', () => ({
1313
1313
  message: 'some resolved external reference',
1314
1314
  }))
1315
1315
  await server.listen({ port: port })
@@ -1321,14 +1321,8 @@ describe('bundle', () => {
1321
1321
  })
1322
1322
 
1323
1323
  expect(result).toEqual({
1324
- 'a': {
1325
- '$ref': '#/x-ext/fb30100',
1326
- },
1327
- 'x-ext': {
1328
- 'fb30100': {
1329
- 'message': 'some resolved external reference',
1330
- },
1331
- },
1324
+ a: { '$ref': '#/x-ext/e53b62c' },
1325
+ 'x-ext': { e53b62c: { message: 'some resolved external reference' } },
1332
1326
  })
1333
1327
  })
1334
1328
  })
@@ -206,6 +206,12 @@ function resolveReferencePath(base: string, relativePath: string) {
206
206
  if (isRemoteUrl(base)) {
207
207
  const url = new URL(base)
208
208
 
209
+ // If the url stars with a / we want it to resolve from the origin so we replace the pathname
210
+ if (relativePath.startsWith('/')) {
211
+ url.pathname = relativePath
212
+ return url.toString()
213
+ }
214
+
209
215
  const mergedPath = path.join(path.dirname(url.pathname), relativePath)
210
216
  return new URL(mergedPath, base).toString()
211
217
  }
@@ -1,6 +1,7 @@
1
- import { createMagicProxy, getRaw } from './proxy'
2
1
  import { describe, expect, it } from 'vitest'
3
2
 
3
+ import { createMagicProxy, getRaw } from './proxy'
4
+
4
5
  describe('createMagicProxy', () => {
5
6
  describe('get', () => {
6
7
  it('should correctly proxy internal refs', () => {
@@ -1137,4 +1138,256 @@ describe('createMagicProxy', () => {
1137
1138
  expect(getRaw(proxied)).toEqual(input)
1138
1139
  })
1139
1140
  })
1141
+
1142
+ describe('show underscore properties when specified', () => {
1143
+ it('should not hide properties starting with underscore from direct access', () => {
1144
+ const input = {
1145
+ public: 'visible',
1146
+ _private: 'hidden',
1147
+ __internal: 'also hidden',
1148
+ normal_underscore: 'visible with underscore in middle',
1149
+ }
1150
+
1151
+ const result = createMagicProxy(input, { showInternal: true })
1152
+
1153
+ expect(result.public).toBe('visible')
1154
+ expect(result._private).toBe('hidden')
1155
+ expect(result.__internal).toBe('also hidden')
1156
+ expect(result.normal_underscore).toBe('visible with underscore in middle')
1157
+ })
1158
+
1159
+ it('should not hide underscore properties from "in" operator', () => {
1160
+ const input = {
1161
+ public: 'visible',
1162
+ _private: 'hidden',
1163
+ __internal: 'also hidden',
1164
+ }
1165
+
1166
+ const result = createMagicProxy(input, { showInternal: true })
1167
+
1168
+ expect('public' in result).toBe(true)
1169
+ expect('_private' in result).toBe(true)
1170
+ expect('__internal' in result).toBe(true)
1171
+ })
1172
+
1173
+ it('should not exclude underscore properties from Object.keys enumeration', () => {
1174
+ const input = {
1175
+ public: 'visible',
1176
+ _private: 'hidden',
1177
+ __internal: 'also hidden',
1178
+ another: 'visible',
1179
+ }
1180
+
1181
+ const result = createMagicProxy(input, { showInternal: true })
1182
+ const keys = Object.keys(result)
1183
+
1184
+ expect(keys).toContain('public')
1185
+ expect(keys).toContain('another')
1186
+ expect(keys).toContain('_private')
1187
+ expect(keys).toContain('__internal')
1188
+ })
1189
+
1190
+ it('should not hide underscore properties from getOwnPropertyDescriptor', () => {
1191
+ const input = {
1192
+ public: 'visible',
1193
+ _private: 'hidden',
1194
+ }
1195
+
1196
+ const result = createMagicProxy(input, { showInternal: true })
1197
+
1198
+ expect(Object.getOwnPropertyDescriptor(result, 'public')).toBeDefined()
1199
+ expect(Object.getOwnPropertyDescriptor(result, '_private')).toBeDefined()
1200
+ })
1201
+
1202
+ it('should not hide underscore properties in nested objects', () => {
1203
+ const input = {
1204
+ nested: {
1205
+ public: 'visible',
1206
+ _private: 'hidden',
1207
+ deeper: {
1208
+ _alsoHidden: 'secret',
1209
+ visible: 'shown',
1210
+ },
1211
+ },
1212
+ _topLevel: 'hidden',
1213
+ }
1214
+
1215
+ const result = createMagicProxy(input, { showInternal: true })
1216
+
1217
+ expect(result._topLevel).toBe('hidden')
1218
+ expect(result.nested.public).toBe('visible')
1219
+ expect(result.nested._private).toBe('hidden')
1220
+ expect(result.nested.deeper._alsoHidden).toBe('secret')
1221
+ expect(result.nested.deeper.visible).toBe('shown')
1222
+ })
1223
+
1224
+ it('should show underscore properties with arrays containing objects with underscore properties', () => {
1225
+ const input = {
1226
+ items: [
1227
+ { public: 'item1', _private: 'hidden1' },
1228
+ { public: 'item2', _private: 'hidden2' },
1229
+ ],
1230
+ }
1231
+
1232
+ const result = createMagicProxy(input, { showInternal: true })
1233
+
1234
+ expect(result.items[0].public).toBe('item1')
1235
+ expect(result.items[0]._private).toBe('hidden1')
1236
+ expect(result.items[1].public).toBe('item2')
1237
+ expect(result.items[1]._private).toBe('hidden2')
1238
+ })
1239
+
1240
+ it('should show underscore ref properties', () => {
1241
+ const input = {
1242
+ definitions: {
1243
+ example: {
1244
+ value: 'hello',
1245
+ _internal: 'hidden',
1246
+ },
1247
+ },
1248
+ _hiddenRef: { $ref: '#/definitions/example' },
1249
+ publicRef: { $ref: '#/definitions/example' },
1250
+ }
1251
+
1252
+ const result = createMagicProxy(input, { showInternal: true })
1253
+
1254
+ // Underscore property should be hidden
1255
+ expect(result._hiddenRef).toEqual({
1256
+ '$ref': '#/definitions/example',
1257
+ '$ref-value': {
1258
+ '_internal': 'hidden',
1259
+ 'value': 'hello',
1260
+ },
1261
+ })
1262
+
1263
+ // Public ref should work normally
1264
+ expect(result.publicRef['$ref-value'].value).toBe('hello')
1265
+
1266
+ // Underscore properties in referenced objects should be hidden
1267
+ expect(result.publicRef['$ref-value']._internal).toBe('hidden')
1268
+ })
1269
+ })
1270
+
1271
+ describe('hide underscore properties', () => {
1272
+ it('should hide properties starting with underscore from direct access', () => {
1273
+ const input = {
1274
+ public: 'visible',
1275
+ _private: 'hidden',
1276
+ __internal: 'also hidden',
1277
+ normal_underscore: 'visible with underscore in middle',
1278
+ }
1279
+
1280
+ const result = createMagicProxy(input)
1281
+
1282
+ expect(result.public).toBe('visible')
1283
+ expect(result._private).toBe(undefined)
1284
+ expect(result.__internal).toBe(undefined)
1285
+ expect(result.normal_underscore).toBe('visible with underscore in middle')
1286
+ })
1287
+
1288
+ it('should hide underscore properties from "in" operator', () => {
1289
+ const input = {
1290
+ public: 'visible',
1291
+ _private: 'hidden',
1292
+ __internal: 'also hidden',
1293
+ }
1294
+
1295
+ const result = createMagicProxy(input)
1296
+
1297
+ expect('public' in result).toBe(true)
1298
+ expect('_private' in result).toBe(false)
1299
+ expect('__internal' in result).toBe(false)
1300
+ })
1301
+
1302
+ it('should exclude underscore properties from Object.keys enumeration', () => {
1303
+ const input = {
1304
+ public: 'visible',
1305
+ _private: 'hidden',
1306
+ __internal: 'also hidden',
1307
+ another: 'visible',
1308
+ }
1309
+
1310
+ const result = createMagicProxy(input)
1311
+ const keys = Object.keys(result)
1312
+
1313
+ expect(keys).toContain('public')
1314
+ expect(keys).toContain('another')
1315
+ expect(keys).not.toContain('_private')
1316
+ expect(keys).not.toContain('__internal')
1317
+ })
1318
+
1319
+ it('should hide underscore properties from getOwnPropertyDescriptor', () => {
1320
+ const input = {
1321
+ public: 'visible',
1322
+ _private: 'hidden',
1323
+ }
1324
+
1325
+ const result = createMagicProxy(input)
1326
+
1327
+ expect(Object.getOwnPropertyDescriptor(result, 'public')).toBeDefined()
1328
+ expect(Object.getOwnPropertyDescriptor(result, '_private')).toBe(undefined)
1329
+ })
1330
+
1331
+ it('should hide underscore properties in nested objects', () => {
1332
+ const input = {
1333
+ nested: {
1334
+ public: 'visible',
1335
+ _private: 'hidden',
1336
+ deeper: {
1337
+ _alsoHidden: 'secret',
1338
+ visible: 'shown',
1339
+ },
1340
+ },
1341
+ _topLevel: 'hidden',
1342
+ }
1343
+
1344
+ const result = createMagicProxy(input)
1345
+
1346
+ expect(result._topLevel).toBe(undefined)
1347
+ expect(result.nested.public).toBe('visible')
1348
+ expect(result.nested._private).toBe(undefined)
1349
+ expect(result.nested.deeper._alsoHidden).toBe(undefined)
1350
+ expect(result.nested.deeper.visible).toBe('shown')
1351
+ })
1352
+
1353
+ it('should work with arrays containing objects with underscore properties', () => {
1354
+ const input = {
1355
+ items: [
1356
+ { public: 'item1', _private: 'hidden1' },
1357
+ { public: 'item2', _private: 'hidden2' },
1358
+ ],
1359
+ }
1360
+
1361
+ const result = createMagicProxy(input)
1362
+
1363
+ expect(result.items[0].public).toBe('item1')
1364
+ expect(result.items[0]._private).toBe(undefined)
1365
+ expect(result.items[1].public).toBe('item2')
1366
+ expect(result.items[1]._private).toBe(undefined)
1367
+ })
1368
+
1369
+ it('should still allow refs to work with underscore hiding', () => {
1370
+ const input = {
1371
+ definitions: {
1372
+ example: {
1373
+ value: 'hello',
1374
+ _internal: 'hidden',
1375
+ },
1376
+ },
1377
+ _hiddenRef: { $ref: '#/definitions/example' },
1378
+ publicRef: { $ref: '#/definitions/example' },
1379
+ }
1380
+
1381
+ const result = createMagicProxy(input)
1382
+
1383
+ // Underscore property should be hidden
1384
+ expect(result._hiddenRef).toBe(undefined)
1385
+
1386
+ // Public ref should work normally
1387
+ expect(result.publicRef['$ref-value'].value).toBe('hello')
1388
+
1389
+ // Underscore properties in referenced objects should be hidden
1390
+ expect(result.publicRef['$ref-value']._internal).toBe(undefined)
1391
+ })
1392
+ })
1140
1393
  })
@@ -18,6 +18,8 @@ const REF_KEY = '$ref'
18
18
  * will resolve and return the referenced value from the root object.
19
19
  * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution
20
20
  * works at any depth.
21
+ * - Properties starting with an underscore (_) are hidden and will not be accessible through
22
+ * the proxy (returns undefined on access, false on 'in' checks, excluded from enumeration).
21
23
  * - Setting, deleting, and enumerating properties works as expected, including for proxied references.
22
24
  *
23
25
  * @param target - The object or array to wrap in a magic proxy
@@ -29,31 +31,45 @@ const REF_KEY = '$ref'
29
31
  * definitions: {
30
32
  * foo: { bar: 123 }
31
33
  * },
32
- * refObj: { $ref: '#/definitions/foo' }
34
+ * refObj: { $ref: '#/definitions/foo' },
35
+ * _internal: 'hidden property'
33
36
  * }
34
37
  * const proxy = createMagicProxy(input)
35
38
  *
36
39
  * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }
37
40
  * console.log(proxy.refObj['$ref-value']) // { bar: 123 }
38
41
  *
42
+ * // Properties starting with underscore are hidden
43
+ * console.log(proxy._internal) // undefined
44
+ * console.log('_internal' in proxy) // false
45
+ * console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')
46
+ *
39
47
  * // Setting and deleting properties works as expected
40
48
  * proxy.refObj.extra = 'hello'
41
49
  * delete proxy.refObj.extra
42
50
  */
43
51
  export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(
44
52
  target: T,
53
+ options?: { showInternal?: boolean },
45
54
  root: S | T = target,
46
55
  cache = new Map<string, unknown>(),
56
+ proxyCache = new WeakMap<object, T>(),
47
57
  ) => {
48
58
  if (!isObject(target) && !Array.isArray(target)) {
49
59
  return target
50
60
  }
51
61
 
62
+ // Return existing proxy for the same target to ensure referential stability
63
+ if (proxyCache.has(target)) {
64
+ return proxyCache.get(target)
65
+ }
66
+
52
67
  const handler: ProxyHandler<T> = {
53
68
  /**
54
69
  * Proxy "get" trap for magic proxy.
55
70
  * - If accessing the special isMagicProxy symbol, return true to identify proxy.
56
71
  * - If accessing the magicProxyTarget symbol, return the original target object.
72
+ * - Hide properties starting with underscore by returning undefined.
57
73
  * - If accessing "$ref-value" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.
58
74
  * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).
59
75
  */
@@ -70,6 +86,12 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
70
86
 
71
87
  const ref = Reflect.get(target, REF_KEY, receiver)
72
88
 
89
+ // Hide properties starting with underscore - these are considered internal/private properties
90
+ // and should not be accessible through the magic proxy interface
91
+ if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
92
+ return undefined
93
+ }
94
+
73
95
  // If accessing "$ref-value" and $ref is a local reference, resolve and return the referenced value
74
96
  if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {
75
97
  // Check cache first for performance optimization
@@ -79,7 +101,7 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
79
101
 
80
102
  // Resolve the reference and create a new magic proxy
81
103
  const resolvedValue = getValueByPath(root, parseJsonPointer(ref))
82
- const proxiedValue = createMagicProxy(resolvedValue, root, cache)
104
+ const proxiedValue = createMagicProxy(resolvedValue, options, root, cache)
83
105
 
84
106
  // Store in cache for future lookups
85
107
  cache.set(ref, proxiedValue)
@@ -88,16 +110,23 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
88
110
 
89
111
  // For all other properties, recursively wrap the value in a magic proxy
90
112
  const value = Reflect.get(target, prop, receiver)
91
- return createMagicProxy(value, root, cache)
113
+ return createMagicProxy(value as T, options, root, cache, proxyCache)
92
114
  },
93
115
  /**
94
116
  * Proxy "set" trap for magic proxy.
95
117
  * Allows setting properties on the proxied object.
96
118
  * This will update the underlying target object.
119
+ *
120
+ * Note: it will not update if the property starts with an underscore (_)
121
+ * Those will be considered private properties by the proxy
97
122
  */
98
123
  set(target, prop, newValue, receiver) {
99
124
  const ref = Reflect.get(target, REF_KEY, receiver)
100
125
 
126
+ if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
127
+ return true
128
+ }
129
+
101
130
  if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {
102
131
  const segments = getSegmentsFromPath(ref)
103
132
 
@@ -131,9 +160,15 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
131
160
  * - Pretend that "$ref-value" exists if "$ref" exists on the target.
132
161
  * This allows expressions like `"$ref-value" in obj` to return true for objects with a $ref,
133
162
  * even though "$ref-value" is a virtual property provided by the proxy.
163
+ * - Hide properties starting with underscore by returning false.
134
164
  * - For all other properties, defer to the default Reflect.has behavior.
135
165
  */
136
166
  has(target, prop) {
167
+ // Hide properties starting with underscore
168
+ if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
169
+ return false
170
+ }
171
+
137
172
  // Pretend that "$ref-value" exists if "$ref" exists
138
173
  if (prop === REF_VALUE && REF_KEY in target) {
139
174
  return true
@@ -146,22 +181,35 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
146
181
  * - If the object has a "$ref" property, ensures that "$ref-value" is also included in the keys,
147
182
  * even though "$ref-value" is a virtual property provided by the proxy.
148
183
  * This allows Object.keys, Reflect.ownKeys, etc. to include "$ref-value" for objects with $ref.
184
+ * - Filters out properties starting with underscore.
149
185
  */
150
186
  ownKeys(target) {
151
187
  const keys = Reflect.ownKeys(target)
152
- if (REF_KEY in target && !keys.includes(REF_VALUE)) {
153
- keys.push(REF_VALUE)
188
+
189
+ // Filter out properties starting with underscore
190
+ const filteredKeys = keys.filter(
191
+ (key) => typeof key !== 'string' || !(key.startsWith('_') && !options?.showInternal),
192
+ )
193
+
194
+ if (REF_KEY in target && !filteredKeys.includes(REF_VALUE)) {
195
+ filteredKeys.push(REF_VALUE)
154
196
  }
155
- return keys
197
+ return filteredKeys
156
198
  },
157
199
 
158
200
  /**
159
201
  * Proxy "getOwnPropertyDescriptor" trap for magic proxy.
160
202
  * - For the virtual "$ref-value" property, returns a descriptor that makes it appear as a regular property.
203
+ * - Hide properties starting with underscore by returning undefined.
161
204
  * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.
162
205
  * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.
163
206
  */
164
207
  getOwnPropertyDescriptor(target, prop) {
208
+ // Hide properties starting with underscore
209
+ if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
210
+ return undefined
211
+ }
212
+
165
213
  const ref = Reflect.get(target, REF_KEY)
166
214
 
167
215
  if (prop === REF_VALUE && typeof ref === 'string') {
@@ -178,7 +226,9 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
178
226
  },
179
227
  }
180
228
 
181
- return new Proxy<T>(target, handler)
229
+ const proxied = new Proxy<T>(target, handler)
230
+ proxyCache.set(target, proxied)
231
+ return proxied
182
232
  }
183
233
 
184
234
  /**