@mearie/core 0.2.3 → 0.2.4

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.
package/dist/index.cjs CHANGED
@@ -638,7 +638,7 @@ var Cache = class {
638
638
  const ss = this.#subscriptions.get(dependency);
639
639
  if (ss) for (const s of ss) subscriptions.add(s);
640
640
  }
641
- for (const subscription of subscriptions) subscription.listener();
641
+ for (const subscription of subscriptions) subscription.listener("write");
642
642
  }
643
643
  /**
644
644
  * Reads a query result from the cache, denormalizing entities if available.
@@ -746,6 +746,7 @@ var Cache = class {
746
746
  delete this.#storage[entityKey];
747
747
  this.#collectSubscriptions(entityKey, void 0, subscriptions);
748
748
  }
749
+ this.#collectLinkedEntitySubscriptions((linkedEntityKey) => linkedEntityKey === entityKey, subscriptions);
749
750
  } else {
750
751
  const prefix = `${target.__typename}:`;
751
752
  for (const key of Object.keys(this.#storage)) if (key.startsWith(prefix)) {
@@ -759,8 +760,18 @@ var Cache = class {
759
760
  this.#collectSubscriptions(entityKey, void 0, subscriptions);
760
761
  }
761
762
  }
763
+ this.#collectLinkedEntitySubscriptions((linkedEntityKey) => linkedEntityKey.startsWith(prefix), subscriptions);
762
764
  }
763
- for (const subscription of subscriptions) subscription.listener();
765
+ for (const subscription of subscriptions) subscription.listener("invalidate");
766
+ }
767
+ #collectLinkedEntitySubscriptions(matcher, out) {
768
+ for (const [storageKey, fields] of Object.entries(this.#storage)) for (const [fieldKey, value] of Object.entries(fields)) if (this.#containsEntityLink(value, matcher)) this.#collectSubscriptions(storageKey, fieldKey, out);
769
+ }
770
+ #containsEntityLink(value, matcher) {
771
+ if (isEntityLink(value)) return matcher(value[EntityLinkKey]);
772
+ if (Array.isArray(value)) return value.some((item) => this.#containsEntityLink(item, matcher));
773
+ if (value && typeof value === "object") return Object.values(value).some((item) => this.#containsEntityLink(item, matcher));
774
+ return false;
764
775
  }
765
776
  #collectSubscriptions(storageKey, fieldKey, out) {
766
777
  if (fieldKey === void 0) {
@@ -853,15 +864,27 @@ const cacheExchange = (options = {}) => {
853
864
  });
854
865
  if (isFragmentRefArray(fragmentRef)) {
855
866
  const trigger = require_make.makeSubject();
867
+ let hasData = false;
856
868
  const teardown$ = require_make.pipe(ops$, require_make.filter((operation) => operation.variant === "teardown" && operation.key === op.key), require_make.tap(() => trigger.complete()));
857
869
  return require_make.pipe(require_make.merge(require_make.fromValue(void 0), trigger.source), require_make.switchMap(() => require_make.fromSubscription(() => cache.readFragments(op.artifact, fragmentRef), () => cache.subscribeFragments(op.artifact, fragmentRef, async () => {
858
870
  await Promise.resolve();
859
871
  trigger.next();
860
- }))), require_make.takeUntil(teardown$), require_make.map((data) => ({
861
- operation: op,
862
- data,
863
- errors: []
864
- })));
872
+ }))), require_make.takeUntil(teardown$), require_make.mergeMap((data) => {
873
+ if (data !== null) {
874
+ hasData = true;
875
+ return require_make.fromValue({
876
+ operation: op,
877
+ data,
878
+ errors: []
879
+ });
880
+ }
881
+ if (hasData) return empty();
882
+ return require_make.fromValue({
883
+ operation: op,
884
+ data,
885
+ errors: []
886
+ });
887
+ }));
865
888
  }
866
889
  if (!isFragmentRef(fragmentRef)) return require_make.fromValue({
867
890
  operation: op,
@@ -869,15 +892,27 @@ const cacheExchange = (options = {}) => {
869
892
  errors: []
870
893
  });
871
894
  const trigger = require_make.makeSubject();
895
+ let hasData = false;
872
896
  const teardown$ = require_make.pipe(ops$, require_make.filter((operation) => operation.variant === "teardown" && operation.key === op.key), require_make.tap(() => trigger.complete()));
873
897
  return require_make.pipe(require_make.merge(require_make.fromValue(void 0), trigger.source), require_make.switchMap(() => require_make.fromSubscription(() => cache.readFragment(op.artifact, fragmentRef), () => cache.subscribeFragment(op.artifact, fragmentRef, async () => {
874
898
  await Promise.resolve();
875
899
  trigger.next();
876
- }))), require_make.takeUntil(teardown$), require_make.map((data) => ({
877
- operation: op,
878
- data,
879
- errors: []
880
- })));
900
+ }))), require_make.takeUntil(teardown$), require_make.mergeMap((data) => {
901
+ if (data !== null) {
902
+ hasData = true;
903
+ return require_make.fromValue({
904
+ operation: op,
905
+ data,
906
+ errors: []
907
+ });
908
+ }
909
+ if (hasData) return empty();
910
+ return require_make.fromValue({
911
+ operation: op,
912
+ data,
913
+ errors: []
914
+ });
915
+ }));
881
916
  }));
882
917
  const nonCache$ = require_make.pipe(ops$, require_make.filter((op) => op.variant === "request" && (op.artifact.kind === "mutation" || op.artifact.kind === "subscription" || op.artifact.kind === "query" && fetchPolicy === "network-only")));
883
918
  const query$ = require_make.pipe(ops$, require_make.filter((op) => op.variant === "request" && op.artifact.kind === "query" && fetchPolicy !== "network-only"), require_make.share());
@@ -885,13 +920,21 @@ const cacheExchange = (options = {}) => {
885
920
  return require_make.merge(fragment$, require_make.pipe(query$, require_make.mergeMap((op) => {
886
921
  const trigger = require_make.makeSubject();
887
922
  let hasData = false;
923
+ let invalidated = false;
888
924
  const teardown$ = require_make.pipe(ops$, require_make.filter((operation) => operation.variant === "teardown" && operation.key === op.key), require_make.tap(() => trigger.complete()));
889
- return require_make.pipe(require_make.merge(require_make.fromValue(void 0), trigger.source), require_make.switchMap(() => require_make.fromSubscription(() => cache.readQuery(op.artifact, op.variables), () => cache.subscribeQuery(op.artifact, op.variables, async () => {
925
+ return require_make.pipe(require_make.merge(require_make.fromValue(void 0), trigger.source), require_make.switchMap(() => require_make.fromSubscription(() => cache.readQuery(op.artifact, op.variables), () => cache.subscribeQuery(op.artifact, op.variables, async (event) => {
926
+ if (event === "invalidate") invalidated = true;
890
927
  await Promise.resolve();
891
928
  trigger.next();
892
929
  }))), require_make.takeUntil(teardown$), require_make.mergeMap((data) => {
893
930
  if (data !== null) {
931
+ if (invalidated && hasData && fetchPolicy !== "cache-only") {
932
+ invalidated = false;
933
+ refetch$.next(op);
934
+ return empty();
935
+ }
894
936
  hasData = true;
937
+ invalidated = false;
895
938
  return require_make.fromValue({
896
939
  operation: op,
897
940
  data,
@@ -899,7 +942,8 @@ const cacheExchange = (options = {}) => {
899
942
  });
900
943
  }
901
944
  if (hasData) {
902
- refetch$.next(op);
945
+ if (fetchPolicy !== "cache-only") refetch$.next(op);
946
+ invalidated = false;
903
947
  return empty();
904
948
  }
905
949
  if (fetchPolicy === "cache-only") return require_make.fromValue({
package/dist/index.mjs CHANGED
@@ -637,7 +637,7 @@ var Cache = class {
637
637
  const ss = this.#subscriptions.get(dependency);
638
638
  if (ss) for (const s of ss) subscriptions.add(s);
639
639
  }
640
- for (const subscription of subscriptions) subscription.listener();
640
+ for (const subscription of subscriptions) subscription.listener("write");
641
641
  }
642
642
  /**
643
643
  * Reads a query result from the cache, denormalizing entities if available.
@@ -745,6 +745,7 @@ var Cache = class {
745
745
  delete this.#storage[entityKey];
746
746
  this.#collectSubscriptions(entityKey, void 0, subscriptions);
747
747
  }
748
+ this.#collectLinkedEntitySubscriptions((linkedEntityKey) => linkedEntityKey === entityKey, subscriptions);
748
749
  } else {
749
750
  const prefix = `${target.__typename}:`;
750
751
  for (const key of Object.keys(this.#storage)) if (key.startsWith(prefix)) {
@@ -758,8 +759,18 @@ var Cache = class {
758
759
  this.#collectSubscriptions(entityKey, void 0, subscriptions);
759
760
  }
760
761
  }
762
+ this.#collectLinkedEntitySubscriptions((linkedEntityKey) => linkedEntityKey.startsWith(prefix), subscriptions);
761
763
  }
762
- for (const subscription of subscriptions) subscription.listener();
764
+ for (const subscription of subscriptions) subscription.listener("invalidate");
765
+ }
766
+ #collectLinkedEntitySubscriptions(matcher, out) {
767
+ for (const [storageKey, fields] of Object.entries(this.#storage)) for (const [fieldKey, value] of Object.entries(fields)) if (this.#containsEntityLink(value, matcher)) this.#collectSubscriptions(storageKey, fieldKey, out);
768
+ }
769
+ #containsEntityLink(value, matcher) {
770
+ if (isEntityLink(value)) return matcher(value[EntityLinkKey]);
771
+ if (Array.isArray(value)) return value.some((item) => this.#containsEntityLink(item, matcher));
772
+ if (value && typeof value === "object") return Object.values(value).some((item) => this.#containsEntityLink(item, matcher));
773
+ return false;
763
774
  }
764
775
  #collectSubscriptions(storageKey, fieldKey, out) {
765
776
  if (fieldKey === void 0) {
@@ -852,15 +863,27 @@ const cacheExchange = (options = {}) => {
852
863
  });
853
864
  if (isFragmentRefArray(fragmentRef)) {
854
865
  const trigger = makeSubject();
866
+ let hasData = false;
855
867
  const teardown$ = pipe(ops$, filter((operation) => operation.variant === "teardown" && operation.key === op.key), tap(() => trigger.complete()));
856
868
  return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readFragments(op.artifact, fragmentRef), () => cache.subscribeFragments(op.artifact, fragmentRef, async () => {
857
869
  await Promise.resolve();
858
870
  trigger.next();
859
- }))), takeUntil(teardown$), map((data) => ({
860
- operation: op,
861
- data,
862
- errors: []
863
- })));
871
+ }))), takeUntil(teardown$), mergeMap((data) => {
872
+ if (data !== null) {
873
+ hasData = true;
874
+ return fromValue({
875
+ operation: op,
876
+ data,
877
+ errors: []
878
+ });
879
+ }
880
+ if (hasData) return empty();
881
+ return fromValue({
882
+ operation: op,
883
+ data,
884
+ errors: []
885
+ });
886
+ }));
864
887
  }
865
888
  if (!isFragmentRef(fragmentRef)) return fromValue({
866
889
  operation: op,
@@ -868,15 +891,27 @@ const cacheExchange = (options = {}) => {
868
891
  errors: []
869
892
  });
870
893
  const trigger = makeSubject();
894
+ let hasData = false;
871
895
  const teardown$ = pipe(ops$, filter((operation) => operation.variant === "teardown" && operation.key === op.key), tap(() => trigger.complete()));
872
896
  return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readFragment(op.artifact, fragmentRef), () => cache.subscribeFragment(op.artifact, fragmentRef, async () => {
873
897
  await Promise.resolve();
874
898
  trigger.next();
875
- }))), takeUntil(teardown$), map((data) => ({
876
- operation: op,
877
- data,
878
- errors: []
879
- })));
899
+ }))), takeUntil(teardown$), mergeMap((data) => {
900
+ if (data !== null) {
901
+ hasData = true;
902
+ return fromValue({
903
+ operation: op,
904
+ data,
905
+ errors: []
906
+ });
907
+ }
908
+ if (hasData) return empty();
909
+ return fromValue({
910
+ operation: op,
911
+ data,
912
+ errors: []
913
+ });
914
+ }));
880
915
  }));
881
916
  const nonCache$ = pipe(ops$, filter((op) => op.variant === "request" && (op.artifact.kind === "mutation" || op.artifact.kind === "subscription" || op.artifact.kind === "query" && fetchPolicy === "network-only")));
882
917
  const query$ = pipe(ops$, filter((op) => op.variant === "request" && op.artifact.kind === "query" && fetchPolicy !== "network-only"), share());
@@ -884,13 +919,21 @@ const cacheExchange = (options = {}) => {
884
919
  return merge(fragment$, pipe(query$, mergeMap((op) => {
885
920
  const trigger = makeSubject();
886
921
  let hasData = false;
922
+ let invalidated = false;
887
923
  const teardown$ = pipe(ops$, filter((operation) => operation.variant === "teardown" && operation.key === op.key), tap(() => trigger.complete()));
888
- return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readQuery(op.artifact, op.variables), () => cache.subscribeQuery(op.artifact, op.variables, async () => {
924
+ return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readQuery(op.artifact, op.variables), () => cache.subscribeQuery(op.artifact, op.variables, async (event) => {
925
+ if (event === "invalidate") invalidated = true;
889
926
  await Promise.resolve();
890
927
  trigger.next();
891
928
  }))), takeUntil(teardown$), mergeMap((data) => {
892
929
  if (data !== null) {
930
+ if (invalidated && hasData && fetchPolicy !== "cache-only") {
931
+ invalidated = false;
932
+ refetch$.next(op);
933
+ return empty();
934
+ }
893
935
  hasData = true;
936
+ invalidated = false;
894
937
  return fromValue({
895
938
  operation: op,
896
939
  data,
@@ -898,7 +941,8 @@ const cacheExchange = (options = {}) => {
898
941
  });
899
942
  }
900
943
  if (hasData) {
901
- refetch$.next(op);
944
+ if (fetchPolicy !== "cache-only") refetch$.next(op);
945
+ invalidated = false;
902
946
  return empty();
903
947
  }
904
948
  if (fetchPolicy === "cache-only") return fromValue({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearie/core",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Type-safe, zero-overhead GraphQL client",
5
5
  "keywords": [
6
6
  "graphql",