{"version":3,"file":"garbageCollection.mjs","names":["retainedQueries: RetainedQueryWithNormalizationAst[]","retainedIds: RetainedIds","links: StoreLink[]"],"sources":["../../src/core/garbageCollection.ts"],"sourcesContent":["import { getParentRecordKey, TYPENAME_FIELD_NAME } from './cache';\nimport type { NormalizationAstNodes, NormalizationAst } from './entrypoint';\nimport type { Variables } from './FragmentReference';\nimport {\n  assertLink,\n  type DataId,\n  type IsographEnvironment,\n  type StoreRecord,\n  type StoreLayerData,\n  type StoreLink,\n  type TypeName,\n} from './IsographEnvironment';\nimport type { BaseStoreLayer } from './optimisticProxy';\nimport {\n  NOT_SET,\n  type PromiseWrapper,\n  type PromiseWrapperOk,\n} from './PromiseWrapper';\n\nexport type RetainedQuery = {\n  readonly normalizationAst: PromiseWrapper<NormalizationAst>;\n  readonly variables: {};\n  readonly root: StoreLink;\n};\n\nexport interface RetainedQueryWithNormalizationAst extends RetainedQuery {\n  readonly normalizationAst: PromiseWrapperOk<NormalizationAst>;\n}\n\nfunction isRetainedQueryWithNormalizationAst(\n  query: RetainedQuery,\n): query is RetainedQueryWithNormalizationAst {\n  return (\n    query.normalizationAst.result !== NOT_SET &&\n    query.normalizationAst.result.kind === 'Ok'\n  );\n}\n\nexport type DidUnretainSomeQuery = boolean;\nexport function unretainQuery(\n  environment: IsographEnvironment,\n  retainedQuery: RetainedQuery,\n): DidUnretainSomeQuery {\n  environment.retainedQueries.delete(retainedQuery);\n  environment.gcBuffer.push(retainedQuery);\n\n  if (environment.gcBuffer.length > environment.gcBufferSize) {\n    environment.gcBuffer.shift();\n    return true;\n  }\n\n  return false;\n}\n\nexport function retainQuery(\n  environment: IsographEnvironment,\n  queryToRetain: RetainedQuery,\n) {\n  environment.retainedQueries.add(queryToRetain);\n  // TODO can we remove this query from the buffer somehow?\n  // We are relying on === equality, but we really should be comparing\n  // id + variables\n}\n\nexport function garbageCollectEnvironment(environment: IsographEnvironment) {\n  if (environment.store.kind !== 'BaseStoreLayer') {\n    return;\n  }\n\n  const retainedQueries: RetainedQueryWithNormalizationAst[] = [];\n  for (const query of environment.retainedQueries) {\n    if (!isRetainedQueryWithNormalizationAst(query)) {\n      return;\n    }\n    retainedQueries.push(query);\n  }\n\n  for (const query of environment.gcBuffer) {\n    if (!isRetainedQueryWithNormalizationAst(query)) {\n      return;\n    }\n    retainedQueries.push(query);\n  }\n\n  garbageCollectBaseStoreLayer(retainedQueries, environment.store);\n}\n\nexport function garbageCollectBaseStoreLayer(\n  retainedQueries: RetainedQueryWithNormalizationAst[],\n  baseStoreLayer: BaseStoreLayer,\n) {\n  const retainedIds: RetainedIds = {};\n\n  for (const query of retainedQueries) {\n    recordReachableIds(baseStoreLayer.data, query, retainedIds);\n  }\n\n  for (const typeName in baseStoreLayer.data) {\n    const dataById = baseStoreLayer.data[typeName];\n    if (dataById == null) continue;\n    const retainedTypeIds = retainedIds[typeName];\n\n    // delete all objects\n    if (retainedTypeIds === undefined || retainedTypeIds.size === 0) {\n      delete baseStoreLayer.data[typeName];\n      continue;\n    }\n\n    for (const dataId in dataById) {\n      if (!retainedTypeIds.has(dataId)) {\n        delete dataById[dataId];\n      }\n    }\n\n    if (Object.keys(dataById).length === 0) {\n      delete baseStoreLayer.data[typeName];\n    }\n  }\n}\n\ninterface RetainedIds {\n  [typeName: TypeName]: Set<DataId>;\n}\n\nfunction recordReachableIds(\n  dataLayer: StoreLayerData,\n  retainedQuery: RetainedQueryWithNormalizationAst,\n  mutableRetainedIds: RetainedIds,\n) {\n  const record =\n    dataLayer[retainedQuery.root.__typename]?.[retainedQuery.root.__link];\n\n  const retainedRecordsIds = (mutableRetainedIds[\n    retainedQuery.root.__typename\n  ] ??= new Set());\n  retainedRecordsIds.add(retainedQuery.root.__link);\n\n  if (record != null) {\n    recordReachableIdsFromRecord(\n      dataLayer,\n      record,\n      mutableRetainedIds,\n      retainedQuery.normalizationAst.result.value.selections,\n      retainedQuery.variables,\n    );\n  }\n}\n\nfunction recordReachableIdsFromRecord(\n  dataLayer: StoreLayerData,\n  currentRecord: StoreRecord,\n  mutableRetainedIds: RetainedIds,\n  selections: NormalizationAstNodes,\n  variables: Variables | null,\n) {\n  for (const selection of selections) {\n    switch (selection.kind) {\n      case 'InlineFragment':\n        if (currentRecord[TYPENAME_FIELD_NAME] === selection.type) {\n          recordReachableIdsFromRecord(\n            dataLayer,\n            currentRecord,\n            mutableRetainedIds,\n            selection.selections,\n            variables,\n          );\n        }\n        continue;\n      case 'Linked':\n        const linkKey = getParentRecordKey(selection, variables ?? {});\n        const linkedFieldOrFields = currentRecord[linkKey];\n\n        const links: StoreLink[] = [];\n        if (Array.isArray(linkedFieldOrFields)) {\n          for (const maybeLink of linkedFieldOrFields) {\n            const link = assertLink(maybeLink);\n            if (link != null) {\n              links.push(link);\n            }\n          }\n        } else {\n          const link = assertLink(linkedFieldOrFields);\n          if (link != null) {\n            links.push(link);\n          }\n        }\n\n        let typeStore =\n          selection.concreteType != null\n            ? dataLayer[selection.concreteType]\n            : null;\n\n        if (typeStore == null && selection.concreteType != null) {\n          continue;\n        }\n\n        for (const nextRecordLink of links) {\n          let __typename = nextRecordLink.__typename;\n\n          const resolvedTypeStore = typeStore ?? dataLayer[__typename];\n\n          if (resolvedTypeStore == null) {\n            continue;\n          }\n\n          const nextRecord = resolvedTypeStore[nextRecordLink.__link];\n          if (nextRecord != null) {\n            const retainedRecordsIds = (mutableRetainedIds[__typename] ??=\n              new Set());\n            retainedRecordsIds.add(nextRecordLink.__link);\n            recordReachableIdsFromRecord(\n              dataLayer,\n              nextRecord,\n              mutableRetainedIds,\n              selection.selections,\n              variables,\n            );\n          }\n        }\n\n        continue;\n      case 'Scalar':\n        continue;\n    }\n  }\n}\n"],"mappings":"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"}