{"version":3,"file":"tracestate-impl.mjs","names":[],"sources":["../../../src/trace/internal/tracestate-impl.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { TraceState } from '../trace_state';\nimport { validateKey, validateValue } from './tracestate-validators';\n\nconst MAX_TRACE_STATE_ITEMS = 32;\nconst MAX_TRACE_STATE_LEN = 512;\nconst LIST_MEMBERS_SEPARATOR = ',';\nconst LIST_MEMBER_KEY_VALUE_SPLITTER = '=';\n\n/**\n * TraceState must be a class and not a simple object type because of the spec\n * requirement (https://www.w3.org/TR/trace-context/#tracestate-field).\n *\n * Here is the list of allowed mutations:\n * - New key-value pair should be added into the beginning of the list\n * - The value of any key can be updated. Modified keys MUST be moved to the\n * beginning of the list.\n *\n * @deprecated Use TraceState from \"@opentelemetry/core\". This will be removed in the next major version.\n */\nexport class TraceStateImpl implements TraceState {\n  private _internalState: Map<string, string> = new Map();\n\n  constructor(rawTraceState?: string) {\n    if (rawTraceState) this._parse(rawTraceState);\n  }\n\n  set(key: string, value: string): TraceStateImpl {\n    // TODO: Benchmark the different approaches(map vs list) and\n    // use the faster one.\n    const currValue = this._internalState.get(key);\n\n    // Get the length the serialized list would have after the mutation\n    // - for existing keys only the length of the value changes\n    // - for new keys it is the key & value length plus\n    //   - +1 for the key/value splitter\n    //   - +1 for the separator if there are other keys\n    let newLength = this.serialize().length;\n    if (typeof currValue === 'string') {\n      newLength += value.length - currValue.length;\n    } else {\n      // A list can hold at most 32 list-members, so a new key is only\n      // accepted while there is room for it. Updating an existing key does\n      // not change the count and is always allowed.\n      if (this._internalState.size >= MAX_TRACE_STATE_ITEMS) {\n        return this;\n      }\n      newLength +=\n        key.length + value.length + (this._internalState.size > 0 ? 2 : 1);\n    }\n    if (newLength > MAX_TRACE_STATE_LEN) {\n      return this;\n    }\n\n    const traceState = this._clone();\n    if (traceState._internalState.has(key)) {\n      traceState._internalState.delete(key);\n    }\n    traceState._internalState.set(key, value);\n    return traceState;\n  }\n\n  unset(key: string): TraceStateImpl {\n    const traceState = this._clone();\n    traceState._internalState.delete(key);\n    return traceState;\n  }\n\n  get(key: string): string | undefined {\n    return this._internalState.get(key);\n  }\n\n  serialize(): string {\n    return (\n      Array.from(this._internalState.keys())\n        // Use reduceRight() because keys are stored in reverse insertion order.\n        .reduceRight((agg: string[], key) => {\n          agg.push(key + LIST_MEMBER_KEY_VALUE_SPLITTER + this.get(key));\n          return agg;\n        }, [])\n        .join(LIST_MEMBERS_SEPARATOR)\n    );\n  }\n\n  private _parse(rawTraceState: string) {\n    if (rawTraceState.length > MAX_TRACE_STATE_LEN) return;\n    this._internalState = rawTraceState\n      .split(LIST_MEMBERS_SEPARATOR)\n      // Use reduceRight() so new keys (.set(...)) will be placed at the beginning\n      .reduceRight((agg: Map<string, string>, part: string) => {\n        const listMember = part.trim(); // Optional Whitespace (OWS) handling\n        const i = listMember.indexOf(LIST_MEMBER_KEY_VALUE_SPLITTER);\n        if (i !== -1) {\n          const key = listMember.slice(0, i);\n          const value = listMember.slice(i + 1, part.length);\n          if (validateKey(key) && validateValue(value)) {\n            agg.set(key, value);\n          } else {\n            // TODO: Consider to add warning log\n          }\n        }\n        return agg;\n      }, new Map());\n\n    // Because of the reverse() requirement, trunc must be done after map is created\n    if (this._internalState.size > MAX_TRACE_STATE_ITEMS) {\n      this._internalState = new Map(\n        Array.from(this._internalState.entries())\n          .reverse() // Use reverse same as original tracestate parse chain\n          .slice(0, MAX_TRACE_STATE_ITEMS)\n      );\n    }\n  }\n\n  // @ts-expect-error TS6133 Accessed in tests only.\n  private _keys(): string[] {\n    return Array.from(this._internalState.keys()).reverse();\n  }\n\n  private _clone(): TraceStateImpl {\n    const traceState = new TraceStateImpl();\n    traceState._internalState = new Map(this._internalState);\n    return traceState;\n  }\n}\n"],"mappings":";;AAQA,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAC5B,MAAM,yBAAyB;AAC/B,MAAM,iCAAiC;;;;;;;;;;;;AAavC,IAAa,iBAAb,MAAa,eAAqC;CAGhD,YAAY,eAAwB;EAFpC,KAAQ,iCAAsC,IAAI,IAAI;EAGpD,IAAI,eAAe,KAAK,OAAO,aAAa;CAC9C;CAEA,IAAI,KAAa,OAA+B;EAG9C,MAAM,YAAY,KAAK,eAAe,IAAI,GAAG;EAO7C,IAAI,YAAY,KAAK,UAAU,CAAC,CAAC;EACjC,IAAI,OAAO,cAAc,UACvB,aAAa,MAAM,SAAS,UAAU;OACjC;GAIL,IAAI,KAAK,eAAe,QAAQ,uBAC9B,OAAO;GAET,aACE,IAAI,SAAS,MAAM,UAAU,KAAK,eAAe,OAAO,IAAI,IAAI;EACpE;EACA,IAAI,YAAY,qBACd,OAAO;EAGT,MAAM,aAAa,KAAK,OAAO;EAC/B,IAAI,WAAW,eAAe,IAAI,GAAG,GACnC,WAAW,eAAe,OAAO,GAAG;EAEtC,WAAW,eAAe,IAAI,KAAK,KAAK;EACxC,OAAO;CACT;CAEA,MAAM,KAA6B;EACjC,MAAM,aAAa,KAAK,OAAO;EAC/B,WAAW,eAAe,OAAO,GAAG;EACpC,OAAO;CACT;CAEA,IAAI,KAAiC;EACnC,OAAO,KAAK,eAAe,IAAI,GAAG;CACpC;CAEA,YAAoB;EAClB,OACE,MAAM,KAAK,KAAK,eAAe,KAAK,CAAC,CAAC,CAEnC,aAAa,KAAe,QAAQ;GACnC,IAAI,KAAK,MAAM,iCAAiC,KAAK,IAAI,GAAG,CAAC;GAC7D,OAAO;EACT,GAAG,CAAC,CAAC,CAAC,CACL,KAAK,sBAAsB;CAElC;CAEA,OAAe,eAAuB;EACpC,IAAI,cAAc,SAAS,qBAAqB;EAChD,KAAK,iBAAiB,cACnB,MAAM,sBAAsB,CAAC,CAE7B,aAAa,KAA0B,SAAiB;GACvD,MAAM,aAAa,KAAK,KAAK;GAC7B,MAAM,IAAI,WAAW,QAAQ,8BAA8B;GAC3D,IAAI,MAAM,IAAI;IACZ,MAAM,MAAM,WAAW,MAAM,GAAG,CAAC;IACjC,MAAM,QAAQ,WAAW,MAAM,IAAI,GAAG,KAAK,MAAM;IACjD,IAAI,YAAY,GAAG,KAAK,cAAc,KAAK,GACzC,IAAI,IAAI,KAAK,KAAK;GAItB;GACA,OAAO;EACT,mBAAG,IAAI,IAAI,CAAC;EAGd,IAAI,KAAK,eAAe,OAAO,uBAC7B,KAAK,iBAAiB,IAAI,IACxB,MAAM,KAAK,KAAK,eAAe,QAAQ,CAAC,CAAC,CACtC,QAAQ,CAAC,CACT,MAAM,GAAG,qBAAqB,CACnC;CAEJ;CAGA,QAA0B;EACxB,OAAO,MAAM,KAAK,KAAK,eAAe,KAAK,CAAC,CAAC,CAAC,QAAQ;CACxD;CAEA,SAAiC;EAC/B,MAAM,aAAa,IAAI,eAAe;EACtC,WAAW,iBAAiB,IAAI,IAAI,KAAK,cAAc;EACvD,OAAO;CACT;AACF"}