{"version":3,"file":"binding.mjs","sources":["../../../../../src/fragment/binding.ts"],"sourcesContent":["import {\n  type Event,\n  type Store,\n  attach,\n  combine,\n  createEvent,\n  createStore,\n  is,\n  sample,\n  scopeBind,\n} from \"effector\"\n\nimport type {\n  ApolloClient,\n  Cache,\n  DocumentNode,\n  OperationVariables,\n  StoreObject,\n  TypedDocumentNode,\n} from \"@apollo/client\"\nimport { type FragmentDefinitionNode, Kind, OperationTypeNode } from \"graphql\"\n\nimport { fragmentName } from \"../lib/name\"\nimport { readonly } from \"../lib/readonly\"\nimport { storify } from \"../lib/storify\"\nimport { setupSubscription } from \"../setup_subscription\"\n\ninterface CreateFragmentBindingOptions<Data, Variables> {\n  /** Your Apollo Client instance. */\n  client: ApolloClient<unknown> | Store<ApolloClient<unknown>>\n  /**\n   * A GraphQL Document with a single `fragment` that you're binding.\n   */\n  document: DocumentNode | TypedDocumentNode<Data, Variables>\n\n  /**\n   * A trigger to setup the binding.\n   *\n   * When called, will start listening to the Apollo Cache. Usually,\n   * this will will be your `appStarted` event.\n   */\n  setup: Event<unknown>\n  /**\n   * A trigger to teardown the binding.\n   *\n   * Acts as a full `reset` action on binding, stopping listening to\n   * Apollo Cache and clearing `$data`.\n   *\n   * You must then call `setup` again to re-activate the binding.\n   */\n  teardown?: Event<unknown>\n\n  /**\n   * A map of Variables that your fragment uses.\n   * Can be omitted if the fragment uses no variables.\n   */\n  variables?: Store<Variables>\n  /**\n   * Define how to identify a specific fragment.\n   *\n   * When provided with `Store<string>`, treat this as a ready-to-use\n   * canonical Cache ID.\n   *\n   * When provided with `Store<StoreObject>`, treat this as an object\n   * with all required _key fields_. It'll be passed to `cache.identify`.\n   */\n  id: Store<string> | Store<StoreObject>\n\n  /** Watch for optimistic updates? */\n  optimistic?: boolean\n\n  /** The name of your binding. Will be derived from the `document` if abscent. */\n  name?: string\n}\n\nexport interface FragmentBinding<Data, Variables> {\n  /**\n   * The fragment data. Will be null if entry was not found in cache, or if\n   * the binding has not been set up yet.\n   */\n  $data: Store<Data | null>\n\n  /**\n   * Is this binding active?\n   *\n   * Only active binding is fully set up, watches cache and keeps `$data` in sync.\n   */\n  $active: Store<boolean>\n\n  meta: {\n    name: string\n    document: TypedDocumentNode<Data, Variables>\n    client: Store<ApolloClient<unknown>>\n  }\n}\n\n/**\n * Allows you to bind a GraphQL fragment to Effector.\n *\n * Creates a live fragment binding to Apollo Cache. When cache updates,\n * so will this binding. This allows you to access on small chunks of\n * cached data.\n *\n * Note: binding _does not_ make a network request. It only binds already\n * existing, cached data so it's accessible from Effector.\n */\nexport function createFragmentBinding<\n  Data,\n  Variables extends OperationVariables = OperationVariables,\n>({\n  client,\n  document,\n\n  setup,\n  teardown,\n\n  id,\n  variables,\n\n  optimistic = true,\n\n  name = fragmentName(document) || \"unknown\",\n}: CreateFragmentBindingOptions<Data, Variables>): FragmentBinding<Data, Variables> {\n  const { fragmentName, typeName } = extractFragment(document)\n\n  const options: Omit<Cache.WatchOptions<Data, Variables>, \"id\" | \"callback\"> = {\n    query: convertToQuery(document, fragmentName),\n    returnPartialData: true,\n    canonizeResults: true,\n    immediate: true,\n    optimistic,\n  }\n\n  const updated = createEvent<Cache.DiffResult<Data>>({ name: `${name}.updated` })\n\n  const $active = createStore<boolean>(false, {\n    name: `${name}.active`,\n    sid: `apollo.${name}.$active`,\n    skipVoid: false,\n  })\n\n  const $data = createStore<Data | null>(null, {\n    name: `${name}.data`,\n    sid: `apollo.${name}.$data`,\n    skipVoid: false,\n  })\n\n  const $client = storify(client, { name: `${name}.client`, sid: `apollo.${name}.$client` })\n\n  const $variables = is.store(variables)\n    ? variables\n    : createStore({} as Variables, {\n        name: `${name}.variables`,\n        sid: `apollo.${name}.$variables`,\n        skipVoid: false,\n      })\n\n  const $id = combine(\n    { client: $client, id },\n    ({ client: { cache }, id }): string =>\n      typeof id === \"string\" ? id : (cache.identify({ __typename: typeName, ...id }) ?? typeName),\n    { skipVoid: false },\n  )\n\n  const subscribeFx = attach({\n    source: { client: $client, id: $id, variables: $variables },\n    effect: ({ client: { cache }, id, variables }) => {\n      const callback = scopeBind(updated, { safe: true })\n\n      return cache.watch({ ...options, variables, id, callback })\n    },\n  })\n\n  $active.on(setup, () => true)\n\n  if (teardown) {\n    $active.on(teardown, () => false)\n    $data.reset(teardown)\n  }\n\n  const connect = sample({\n    clock: [$active, $client, $id, $variables],\n    filter: $active,\n    fn: (): void => undefined,\n  })\n\n  sample({\n    clock: updated,\n    fn: ({ complete, result }) => (complete && result ? result : null),\n    target: $data,\n  })\n\n  setupSubscription({ setup: connect, teardown, subscribe: subscribeFx, name })\n\n  return {\n    $data: readonly($data),\n    $active: readonly($active),\n\n    meta: { name, document, client: $client },\n  }\n}\n\nfunction extractFragment(document: DocumentNode) {\n  const fragment = document.definitions.find(\n    (node): node is FragmentDefinitionNode => node.kind === Kind.FRAGMENT_DEFINITION,\n  )\n\n  if (!fragment) throw new Error(`Provided document does not contain a fragment`)\n\n  const fragmentName = fragment.name.value\n  const typeName = fragment.typeCondition.name.value\n\n  return { fragmentName, typeName }\n}\n\nfunction convertToQuery(document: DocumentNode, fragmentName: string): DocumentNode {\n  return {\n    ...document,\n    definitions: [\n      {\n        kind: Kind.OPERATION_DEFINITION,\n        operation: OperationTypeNode.QUERY,\n        selectionSet: {\n          kind: Kind.SELECTION_SET,\n          selections: [\n            {\n              kind: Kind.FRAGMENT_SPREAD,\n              name: { kind: Kind.NAME, value: fragmentName },\n            },\n          ],\n        },\n      },\n      ...document.definitions,\n    ],\n  }\n}\n"],"names":["fragmentName","id","variables"],"mappings":"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"}