{"version":3,"file":"factory.mjs","sources":["../../src/traverse/factory.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-loop-func */\nimport {\n  AnyAttribute,\n  Attribute,\n  ComponentAttribute,\n  DynamicZoneAttribute,\n  Model,\n  RelationalAttribute,\n} from '../types';\n\nexport interface Path {\n  raw: string | null;\n  attribute: string | null;\n}\n\nexport interface Parent {\n  attribute?: Attribute;\n  key: string | null;\n  path: Path;\n  schema: Model;\n}\n\nexport interface TraverseOptions {\n  schema: Model;\n  path?: Path;\n  parent?: Parent;\n  getModel(uid: string): Model;\n}\n\nexport interface VisitorOptions {\n  data: unknown;\n  value: unknown;\n  schema: Model;\n  key: string;\n  attribute?: AnyAttribute;\n  path: Path;\n  parent?: Parent;\n  getModel(uid: string): Model;\n}\n\nexport type Traverse = (\n  visitor: Visitor,\n  options: TraverseOptions,\n  data: unknown\n) => Promise<unknown>;\n\nexport interface Visitor {\n  (visitorOptions: VisitorOptions, opts: Pick<TransformUtils, 'set' | 'remove'>): void;\n}\n\ninterface Interceptor<T = unknown> {\n  predicate(data: unknown): data is T;\n  handler(\n    visitor: Visitor,\n    options: TraverseOptions,\n    data: T,\n    recurseOptions: { recurse: Traverse }\n  ): void;\n}\n\ninterface ParseUtils<T> {\n  transform(data: T): unknown;\n  remove(key: string, data: T): unknown;\n  set(key: string, value: unknown, data: T): unknown;\n  keys(data: T): string[];\n  get(key: string, data: T): unknown;\n}\n\ninterface Parser<T = unknown> {\n  predicate(data: unknown): data is T;\n  parser(data: T): ParseUtils<T>;\n}\n\ninterface Ignore {\n  (ctx: Context): boolean;\n}\n\ninterface AttributeHandler<AttributeType = Attribute> {\n  predicate(ctx: Context<AttributeType>): boolean;\n  handler(ctx: Context<AttributeType>, opts: Pick<TransformUtils, 'set' | 'recurse'>): void;\n}\ninterface CommonHandler<AttributeType = Attribute> {\n  predicate(ctx: Context<AttributeType>): boolean;\n  handler(ctx: Context<AttributeType>, opts: Pick<TransformUtils, 'set' | 'recurse'>): void;\n}\n\nexport interface TransformUtils {\n  remove(key: string): void;\n  set(key: string, value: unknown): void;\n  recurse: Traverse;\n}\n\ninterface Context<AttributeType = Attribute> {\n  key: string;\n  value: unknown;\n  attribute: AttributeType;\n  schema: Model;\n  path: Path;\n  data: unknown;\n  visitor: Visitor;\n  parent?: Parent;\n  getModel(uid: string): Model;\n}\n\ninterface State {\n  parsers: Parser[];\n  interceptors: Interceptor[];\n  ignore: Ignore[];\n  handlers: {\n    attributes: AttributeHandler[];\n    common: CommonHandler[];\n  };\n}\n\nconst DEFAULT_PATH = { raw: null, attribute: null } satisfies Path;\n\nexport default () => {\n  const state: State = {\n    parsers: [],\n    interceptors: [],\n    ignore: [],\n    handlers: {\n      attributes: [],\n      common: [],\n    },\n  };\n\n  const traverse: Traverse = async (visitor, options, data) => {\n    const { path = DEFAULT_PATH, parent, schema, getModel } = options ?? {};\n\n    // interceptors\n    for (const { predicate, handler } of state.interceptors) {\n      if (predicate(data)) {\n        return handler(visitor, options, data, { recurse: traverse });\n      }\n    }\n\n    // parsers\n    const parser = state.parsers.find((parser) => parser.predicate(data))?.parser;\n    const utils = parser?.(data);\n\n    // Return the data untouched if we don't know how to traverse it\n    if (!utils) {\n      return data;\n    }\n\n    // main loop\n    let out = utils.transform(data);\n    const keys = utils.keys(out);\n\n    // Built once per traversal rather than once per key. `state.handlers` is populated\n    // while the traversal is being defined and is fixed by the time it runs, so spreading\n    // both lists on every key allocated a fresh array to hold the same handlers. This\n    // loop runs for every key of every node of every query, which made it one of the\n    // hottest allocation sites in a request.\n    const handlers = [...state.handlers.common, ...state.handlers.attributes];\n\n    // Also hoisted: none of these close over `key` (each method takes it as an argument),\n    // so the previous code allocated three closures per key to no end. They still mutate\n    // the same `out` binding, so behaviour is unchanged.\n    const transformUtils: TransformUtils = {\n      remove(key) {\n        out = utils.remove(key, out);\n      },\n      set(key, value) {\n        out = utils.set(key, value, out);\n      },\n      recurse: traverse,\n    };\n\n    // Pre-picked instead of calling lodash `pick` per key, which allocated both the\n    // key-list array literal and a fresh result object every time.\n    const visitorUtils: Pick<TransformUtils, 'set' | 'remove'> = {\n      remove: transformUtils.remove,\n      set: transformUtils.set,\n    };\n    const handlerUtils: Pick<TransformUtils, 'set' | 'recurse'> = {\n      set: transformUtils.set,\n      recurse: transformUtils.recurse,\n    };\n\n    for (const key of keys) {\n      const attribute = schema?.attributes?.[key];\n\n      // The attribute segment only grows when the key maps to an attribute; otherwise the\n      // parent's value carries through unchanged.\n      let attributePath = path.attribute;\n      if (attribute != null) {\n        attributePath = path.attribute == null ? key : `${path.attribute}.${key}`;\n      }\n\n      // A single literal rather than a spread followed by conditional assignment: the\n      // spread produced an object whose hidden class then transitioned on each write.\n      // `Path` is closed over `raw` and `attribute` and callers never supply their own,\n      // so nothing can be dropped by building it directly.\n      const newPath: Path = {\n        raw: path.raw == null ? key : `${path.raw}.${key}`,\n        attribute: attributePath,\n      };\n\n      // visitors\n      const visitorOptions: VisitorOptions = {\n        key,\n        value: utils.get(key, out),\n        attribute,\n        schema,\n        path: newPath,\n        data: out,\n        getModel,\n        parent,\n      };\n\n      // Awaited unconditionally. Most visitors are synchronous, and awaiting a\n      // non-thenable still schedules a microtask, but skipping that await changes\n      // the order of branches that recurse concurrently via Promise.all.\n      //\n      // That reordering used to drop nested populate in content-manager's\n      // getQueryPopulate: a shallow `relation` write replaced the subtree a deeper\n      // sibling had written at `relation.populate.component`. #27236 fixed that\n      // visitor by merging paths. Re-measuring the skip afterward showed no\n      // throughput gain, so the await stays.\n      await visitor(visitorOptions, visitorUtils);\n\n      const value = utils.get(key, out);\n\n      const createContext = (): Context => ({\n        key,\n        value,\n        attribute,\n        schema,\n        path: newPath,\n        data: out,\n        visitor,\n        getModel,\n        parent,\n      });\n\n      // ignore\n      const ignoreCtx = createContext();\n      const shouldIgnore = state.ignore.some((predicate) => predicate(ignoreCtx));\n\n      if (shouldIgnore) {\n        continue;\n      }\n\n      // handlers\n      //\n      // `for await` over a plain array awaits every element, allocating a promise per\n      // handler per key even though the elements are ordinary objects. A plain `for...of`\n      // with explicit thenable checks is equivalent for non-promise elements, and the\n      // predicates in practice are synchronous.\n      for (const handler of handlers) {\n        const ctx = createContext();\n\n        // A fresh context per handler is deliberate: a handler may call `set`, which\n        // reassigns `out`, and the next handler must observe the updated `data`.\n        const predicated = handler.predicate(ctx) as unknown;\n        const pass =\n          predicated != null && typeof (predicated as PromiseLike<boolean>).then === 'function'\n            ? await (predicated as PromiseLike<boolean>)\n            : predicated;\n\n        if (pass) {\n          const handled = handler.handler(ctx, handlerUtils) as unknown;\n\n          if (handled != null && typeof (handled as PromiseLike<void>).then === 'function') {\n            await handled;\n          }\n        }\n      }\n    }\n\n    return out;\n  };\n\n  return {\n    traverse,\n\n    intercept<T>(predicate: Interceptor<T>['predicate'], handler: Interceptor<T>['handler']) {\n      state.interceptors.push({ predicate, handler });\n      return this;\n    },\n\n    parse<T>(predicate: Parser<T>['predicate'], parser: Parser<T>['parser']) {\n      state.parsers.push({ predicate, parser });\n      return this;\n    },\n\n    ignore(predicate: Ignore) {\n      state.ignore.push(predicate);\n      return this;\n    },\n\n    on(predicate: CommonHandler['predicate'], handler: CommonHandler['handler']) {\n      state.handlers.common.push({ predicate, handler });\n      return this;\n    },\n\n    onAttribute(predicate: AttributeHandler['predicate'], handler: AttributeHandler['handler']) {\n      state.handlers.attributes.push({ predicate, handler });\n      return this;\n    },\n\n    onRelation(handler: AttributeHandler<RelationalAttribute>['handler']) {\n      return this.onAttribute(({ attribute }) => attribute?.type === 'relation', handler);\n    },\n\n    onMedia(handler: AttributeHandler<RelationalAttribute>['handler']) {\n      return this.onAttribute(({ attribute }) => attribute?.type === 'media', handler);\n    },\n\n    onComponent(handler: AttributeHandler<ComponentAttribute>['handler']) {\n      return this.onAttribute(({ attribute }) => attribute?.type === 'component', handler);\n    },\n\n    onDynamicZone(handler: AttributeHandler<DynamicZoneAttribute>['handler']) {\n      return this.onAttribute(({ attribute }) => attribute?.type === 'dynamiczone', handler);\n    },\n  };\n};\n"],"names":["DEFAULT_PATH","raw","attribute","state","parsers","interceptors","ignore","handlers","attributes","common","traverse","visitor","options","data","path","parent","schema","getModel","predicate","handler","recurse","parser","find","utils","out","transform","keys","transformUtils","remove","key","set","value","visitorUtils","handlerUtils","attributePath","newPath","visitorOptions","get","createContext","ignoreCtx","shouldIgnore","some","ctx","predicated","pass","then","handled","intercept","push","parse","on","onAttribute","onRelation","type","onMedia","onComponent","onDynamicZone"],"mappings":"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"}