@mearie/core 0.2.3 → 0.3.0
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 +77 -29
- package/dist/index.d.cts +8 -2
- package/dist/index.d.mts +8 -2
- package/dist/index.mjs +77 -29
- package/package.json +11 -4
package/dist/index.cjs
CHANGED
|
@@ -594,8 +594,10 @@ const denormalize = (selections, storage, value, variables, accessor) => {
|
|
|
594
594
|
const value = selection.selections ? denormalizeField(null, selection.selections, fieldValue) : fieldValue;
|
|
595
595
|
if (name in fields) mergeFields(fields, { [name]: value });
|
|
596
596
|
else fields[name] = value;
|
|
597
|
-
} else if (selection.kind === "FragmentSpread") if (storageKey !== null && storageKey !== RootFieldKey)
|
|
598
|
-
|
|
597
|
+
} else if (selection.kind === "FragmentSpread") if (storageKey !== null && storageKey !== RootFieldKey) {
|
|
598
|
+
fields[FragmentRefKey] = storageKey;
|
|
599
|
+
if (accessor) denormalize(selection.selections, storage, { [EntityLinkKey]: storageKey }, variables, accessor);
|
|
600
|
+
} else mergeFields(fields, denormalizeField(storageKey, selection.selections, value));
|
|
599
601
|
else if (selection.kind === "InlineFragment" && selection.on === data[typenameFieldKey]) mergeFields(fields, denormalizeField(storageKey, selection.selections, value));
|
|
600
602
|
return fields;
|
|
601
603
|
};
|
|
@@ -616,6 +618,7 @@ var Cache = class {
|
|
|
616
618
|
#storage = { [RootFieldKey]: {} };
|
|
617
619
|
#subscriptions = /* @__PURE__ */ new Map();
|
|
618
620
|
#memo = /* @__PURE__ */ new Map();
|
|
621
|
+
#stale = /* @__PURE__ */ new Set();
|
|
619
622
|
constructor(schemaMetadata) {
|
|
620
623
|
this.#schemaMeta = schemaMetadata;
|
|
621
624
|
}
|
|
@@ -628,12 +631,14 @@ var Cache = class {
|
|
|
628
631
|
writeQuery(artifact, variables, data) {
|
|
629
632
|
const dependencies = /* @__PURE__ */ new Set();
|
|
630
633
|
const subscriptions = /* @__PURE__ */ new Set();
|
|
634
|
+
const entityStaleCleared = /* @__PURE__ */ new Set();
|
|
631
635
|
normalize(this.#schemaMeta, artifact.selections, this.#storage, data, variables, (storageKey, fieldKey, oldValue, newValue) => {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
+
const depKey = makeDependencyKey(storageKey, fieldKey);
|
|
637
|
+
if (this.#stale.delete(depKey)) dependencies.add(depKey);
|
|
638
|
+
if (!entityStaleCleared.has(storageKey) && this.#stale.delete(storageKey)) entityStaleCleared.add(storageKey);
|
|
639
|
+
if (oldValue !== newValue) dependencies.add(depKey);
|
|
636
640
|
});
|
|
641
|
+
for (const entityKey of entityStaleCleared) this.#collectSubscriptions(entityKey, void 0, subscriptions);
|
|
637
642
|
for (const dependency of dependencies) {
|
|
638
643
|
const ss = this.#subscriptions.get(dependency);
|
|
639
644
|
if (ss) for (const s of ss) subscriptions.add(s);
|
|
@@ -648,13 +653,22 @@ var Cache = class {
|
|
|
648
653
|
* @returns Denormalized query result or null if not found.
|
|
649
654
|
*/
|
|
650
655
|
readQuery(artifact, variables) {
|
|
651
|
-
|
|
652
|
-
|
|
656
|
+
let stale = false;
|
|
657
|
+
const { data, partial } = denormalize(artifact.selections, this.#storage, this.#storage[RootFieldKey], variables, (storageKey, fieldKey) => {
|
|
658
|
+
if (this.#stale.has(storageKey) || this.#stale.has(makeDependencyKey(storageKey, fieldKey))) stale = true;
|
|
659
|
+
});
|
|
660
|
+
if (partial) return {
|
|
661
|
+
data: null,
|
|
662
|
+
stale: false
|
|
663
|
+
};
|
|
653
664
|
const key = makeMemoKey("query", artifact.name, stringify(variables));
|
|
654
665
|
const prev = this.#memo.get(key);
|
|
655
666
|
const result = prev === void 0 ? data : replaceEqualDeep(prev, data);
|
|
656
667
|
this.#memo.set(key, result);
|
|
657
|
-
return
|
|
668
|
+
return {
|
|
669
|
+
data: result,
|
|
670
|
+
stale
|
|
671
|
+
};
|
|
658
672
|
}
|
|
659
673
|
/**
|
|
660
674
|
* Subscribes to cache invalidations for a specific query.
|
|
@@ -680,14 +694,26 @@ var Cache = class {
|
|
|
680
694
|
*/
|
|
681
695
|
readFragment(artifact, fragmentRef) {
|
|
682
696
|
const entityKey = fragmentRef[FragmentRefKey];
|
|
683
|
-
if (!this.#storage[entityKey]) return
|
|
684
|
-
|
|
685
|
-
|
|
697
|
+
if (!this.#storage[entityKey]) return {
|
|
698
|
+
data: null,
|
|
699
|
+
stale: false
|
|
700
|
+
};
|
|
701
|
+
let stale = false;
|
|
702
|
+
const { data, partial } = denormalize(artifact.selections, this.#storage, { [EntityLinkKey]: entityKey }, {}, (storageKey, fieldKey) => {
|
|
703
|
+
if (this.#stale.has(storageKey) || this.#stale.has(makeDependencyKey(storageKey, fieldKey))) stale = true;
|
|
704
|
+
});
|
|
705
|
+
if (partial) return {
|
|
706
|
+
data: null,
|
|
707
|
+
stale: false
|
|
708
|
+
};
|
|
686
709
|
const key = makeMemoKey("fragment", artifact.name, entityKey);
|
|
687
710
|
const prev = this.#memo.get(key);
|
|
688
711
|
const result = prev === void 0 ? data : replaceEqualDeep(prev, data);
|
|
689
712
|
this.#memo.set(key, result);
|
|
690
|
-
return
|
|
713
|
+
return {
|
|
714
|
+
data: result,
|
|
715
|
+
stale
|
|
716
|
+
};
|
|
691
717
|
}
|
|
692
718
|
subscribeFragment(artifact, fragmentRef, listener) {
|
|
693
719
|
const entityKey = fragmentRef[FragmentRefKey];
|
|
@@ -700,17 +726,25 @@ var Cache = class {
|
|
|
700
726
|
}
|
|
701
727
|
readFragments(artifact, fragmentRefs) {
|
|
702
728
|
const results = [];
|
|
729
|
+
let stale = false;
|
|
703
730
|
for (const ref of fragmentRefs) {
|
|
704
|
-
const
|
|
705
|
-
if (data === null) return
|
|
706
|
-
|
|
731
|
+
const result = this.readFragment(artifact, ref);
|
|
732
|
+
if (result.data === null) return {
|
|
733
|
+
data: null,
|
|
734
|
+
stale: false
|
|
735
|
+
};
|
|
736
|
+
if (result.stale) stale = true;
|
|
737
|
+
results.push(result.data);
|
|
707
738
|
}
|
|
708
739
|
const entityKeys = fragmentRefs.map((ref) => ref[FragmentRefKey]);
|
|
709
740
|
const key = makeMemoKey("fragments", artifact.name, entityKeys.join(","));
|
|
710
741
|
const prev = this.#memo.get(key);
|
|
711
742
|
const result = prev === void 0 ? results : replaceEqualDeep(prev, results);
|
|
712
743
|
this.#memo.set(key, result);
|
|
713
|
-
return
|
|
744
|
+
return {
|
|
745
|
+
data: result,
|
|
746
|
+
stale
|
|
747
|
+
};
|
|
714
748
|
}
|
|
715
749
|
subscribeFragments(artifact, fragmentRefs, listener) {
|
|
716
750
|
const dependencies = /* @__PURE__ */ new Set();
|
|
@@ -730,20 +764,21 @@ var Cache = class {
|
|
|
730
764
|
const subscriptions = /* @__PURE__ */ new Set();
|
|
731
765
|
for (const target of targets) if (target.__typename === "Query") if ("field" in target) {
|
|
732
766
|
const fieldKey = makeFieldKeyFromArgs(target.field, target.args);
|
|
733
|
-
|
|
767
|
+
const depKey = makeDependencyKey(RootFieldKey, fieldKey);
|
|
768
|
+
this.#stale.add(depKey);
|
|
734
769
|
this.#collectSubscriptions(RootFieldKey, fieldKey, subscriptions);
|
|
735
770
|
} else {
|
|
736
|
-
this.#
|
|
771
|
+
this.#stale.add(RootFieldKey);
|
|
737
772
|
this.#collectSubscriptions(RootFieldKey, void 0, subscriptions);
|
|
738
773
|
}
|
|
739
774
|
else if ("id" in target) {
|
|
740
775
|
const entityKey = resolveEntityKey(target.__typename, target.id, this.#schemaMeta.entities[target.__typename]?.keyFields);
|
|
741
776
|
if ("field" in target) {
|
|
742
777
|
const fieldKey = makeFieldKeyFromArgs(target.field, target.args);
|
|
743
|
-
|
|
778
|
+
this.#stale.add(makeDependencyKey(entityKey, fieldKey));
|
|
744
779
|
this.#collectSubscriptions(entityKey, fieldKey, subscriptions);
|
|
745
780
|
} else {
|
|
746
|
-
|
|
781
|
+
this.#stale.add(entityKey);
|
|
747
782
|
this.#collectSubscriptions(entityKey, void 0, subscriptions);
|
|
748
783
|
}
|
|
749
784
|
} else {
|
|
@@ -752,10 +787,10 @@ var Cache = class {
|
|
|
752
787
|
const entityKey = key;
|
|
753
788
|
if ("field" in target) {
|
|
754
789
|
const fieldKey = makeFieldKeyFromArgs(target.field, target.args);
|
|
755
|
-
|
|
790
|
+
this.#stale.add(makeDependencyKey(entityKey, fieldKey));
|
|
756
791
|
this.#collectSubscriptions(entityKey, fieldKey, subscriptions);
|
|
757
792
|
} else {
|
|
758
|
-
|
|
793
|
+
this.#stale.add(entityKey);
|
|
759
794
|
this.#collectSubscriptions(entityKey, void 0, subscriptions);
|
|
760
795
|
}
|
|
761
796
|
}
|
|
@@ -814,6 +849,7 @@ var Cache = class {
|
|
|
814
849
|
this.#storage = { [RootFieldKey]: {} };
|
|
815
850
|
this.#subscriptions.clear();
|
|
816
851
|
this.#memo.clear();
|
|
852
|
+
this.#stale.clear();
|
|
817
853
|
}
|
|
818
854
|
};
|
|
819
855
|
|
|
@@ -857,9 +893,10 @@ const cacheExchange = (options = {}) => {
|
|
|
857
893
|
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
894
|
await Promise.resolve();
|
|
859
895
|
trigger.next();
|
|
860
|
-
}))), require_make.takeUntil(teardown$), require_make.map((data) => ({
|
|
896
|
+
}))), require_make.takeUntil(teardown$), require_make.map(({ data, stale }) => ({
|
|
861
897
|
operation: op,
|
|
862
898
|
data,
|
|
899
|
+
...stale && { metadata: { cache: { stale: true } } },
|
|
863
900
|
errors: []
|
|
864
901
|
})));
|
|
865
902
|
}
|
|
@@ -873,9 +910,10 @@ const cacheExchange = (options = {}) => {
|
|
|
873
910
|
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
911
|
await Promise.resolve();
|
|
875
912
|
trigger.next();
|
|
876
|
-
}))), require_make.takeUntil(teardown$), require_make.map((data) => ({
|
|
913
|
+
}))), require_make.takeUntil(teardown$), require_make.map(({ data, stale }) => ({
|
|
877
914
|
operation: op,
|
|
878
915
|
data,
|
|
916
|
+
...stale && { metadata: { cache: { stale: true } } },
|
|
879
917
|
errors: []
|
|
880
918
|
})));
|
|
881
919
|
}));
|
|
@@ -889,8 +927,8 @@ const cacheExchange = (options = {}) => {
|
|
|
889
927
|
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 () => {
|
|
890
928
|
await Promise.resolve();
|
|
891
929
|
trigger.next();
|
|
892
|
-
}))), require_make.takeUntil(teardown$), require_make.mergeMap((data) => {
|
|
893
|
-
if (data !== null) {
|
|
930
|
+
}))), require_make.takeUntil(teardown$), require_make.mergeMap(({ data, stale }) => {
|
|
931
|
+
if (data !== null && !stale) {
|
|
894
932
|
hasData = true;
|
|
895
933
|
return require_make.fromValue({
|
|
896
934
|
operation: op,
|
|
@@ -898,6 +936,16 @@ const cacheExchange = (options = {}) => {
|
|
|
898
936
|
errors: []
|
|
899
937
|
});
|
|
900
938
|
}
|
|
939
|
+
if (data !== null && stale) {
|
|
940
|
+
hasData = true;
|
|
941
|
+
refetch$.next(op);
|
|
942
|
+
return require_make.fromValue({
|
|
943
|
+
operation: op,
|
|
944
|
+
data,
|
|
945
|
+
metadata: { cache: { stale: true } },
|
|
946
|
+
errors: []
|
|
947
|
+
});
|
|
948
|
+
}
|
|
901
949
|
if (hasData) {
|
|
902
950
|
refetch$.next(op);
|
|
903
951
|
return empty();
|
|
@@ -910,8 +958,8 @@ const cacheExchange = (options = {}) => {
|
|
|
910
958
|
return empty();
|
|
911
959
|
}));
|
|
912
960
|
}), require_make.filter(() => fetchPolicy === "cache-only" || fetchPolicy === "cache-and-network" || fetchPolicy === "cache-first")), require_make.pipe(require_make.merge(nonCache$, require_make.pipe(query$, require_make.filter((op) => {
|
|
913
|
-
const
|
|
914
|
-
return fetchPolicy === "cache-and-network" ||
|
|
961
|
+
const { data } = cache.readQuery(op.artifact, op.variables);
|
|
962
|
+
return fetchPolicy === "cache-and-network" || data === null;
|
|
915
963
|
})), require_make.pipe(ops$, require_make.filter((op) => op.variant === "teardown")), refetch$.source), forward, require_make.tap((result) => {
|
|
916
964
|
if (result.operation.variant === "request" && result.data) cache.writeQuery(result.operation.artifact, result.operation.variables, result.data);
|
|
917
965
|
}), require_make.filter((result) => result.operation.variant !== "request" || result.operation.artifact.kind !== "query" || fetchPolicy === "network-only" || !!(result.errors && result.errors.length > 0))));
|
package/dist/index.d.cts
CHANGED
|
@@ -116,6 +116,7 @@ declare const createClient: <T extends SchemaMeta$1>(config: ClientOptions<T>) =
|
|
|
116
116
|
//#endregion
|
|
117
117
|
//#region src/exchange.d.ts
|
|
118
118
|
interface OperationMetadataMap {}
|
|
119
|
+
interface OperationResultMetadataMap {}
|
|
119
120
|
type OperationMetadata = { [K in keyof OperationMetadataMap]?: OperationMetadataMap[K] } & Record<string, unknown>;
|
|
120
121
|
type BaseOperation = {
|
|
121
122
|
key: string;
|
|
@@ -135,7 +136,7 @@ type OperationResult = {
|
|
|
135
136
|
data?: unknown;
|
|
136
137
|
errors?: readonly OperationError[];
|
|
137
138
|
extensions?: Record<string, unknown>;
|
|
138
|
-
|
|
139
|
+
metadata?: OperationResultMetadataMap & Record<string, unknown>;
|
|
139
140
|
};
|
|
140
141
|
type ExchangeInput<TMeta extends SchemaMeta$1 = SchemaMeta$1> = {
|
|
141
142
|
forward: ExchangeIO;
|
|
@@ -243,6 +244,11 @@ declare module '@mearie/core' {
|
|
|
243
244
|
interface ExchangeExtensionMap {
|
|
244
245
|
cache: CacheOperations;
|
|
245
246
|
}
|
|
247
|
+
interface OperationResultMetadataMap {
|
|
248
|
+
cache?: {
|
|
249
|
+
stale: boolean;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
246
252
|
}
|
|
247
253
|
type CacheOptions = {
|
|
248
254
|
fetchPolicy?: 'cache-first' | 'cache-and-network' | 'network-only' | 'cache-only';
|
|
@@ -345,4 +351,4 @@ declare class RequiredFieldError extends Error {
|
|
|
345
351
|
*/
|
|
346
352
|
declare const stringify: (value: unknown) => string;
|
|
347
353
|
//#endregion
|
|
348
|
-
export { AggregatedError, type Artifact, type ArtifactKind, type CacheOptions, type CacheSnapshot, Client, type ClientOptions, type DataOf, type Exchange, ExchangeError, type ExchangeErrorExtensionsMap, type ExchangeExtensionMap, type ExchangeIO, type ExchangeResult, type FragmentOptions, type FragmentRefs, GraphQLError, type HttpOptions, type MutationOptions, type Operation, type OperationError, type OperationMetadata, type OperationMetadataMap, type OperationResult, type QueryOptions, type RequiredAction, RequiredFieldError, type RetryOptions, type SchemaMeta, type SubscriptionClient, type SubscriptionExchangeOptions, type SubscriptionOptions, type VariablesOf, cacheExchange, createClient, dedupExchange, fragmentExchange, httpExchange, isAggregatedError, isExchangeError, isGraphQLError, requiredExchange, retryExchange, stringify, subscriptionExchange };
|
|
354
|
+
export { AggregatedError, type Artifact, type ArtifactKind, type CacheOptions, type CacheSnapshot, Client, type ClientOptions, type DataOf, type Exchange, ExchangeError, type ExchangeErrorExtensionsMap, type ExchangeExtensionMap, type ExchangeIO, type ExchangeResult, type FragmentOptions, type FragmentRefs, GraphQLError, type HttpOptions, type MutationOptions, type Operation, type OperationError, type OperationMetadata, type OperationMetadataMap, type OperationResult, type OperationResultMetadataMap, type QueryOptions, type RequiredAction, RequiredFieldError, type RetryOptions, type SchemaMeta, type SubscriptionClient, type SubscriptionExchangeOptions, type SubscriptionOptions, type VariablesOf, cacheExchange, createClient, dedupExchange, fragmentExchange, httpExchange, isAggregatedError, isExchangeError, isGraphQLError, requiredExchange, retryExchange, stringify, subscriptionExchange };
|
package/dist/index.d.mts
CHANGED
|
@@ -116,6 +116,7 @@ declare const createClient: <T extends SchemaMeta$1>(config: ClientOptions<T>) =
|
|
|
116
116
|
//#endregion
|
|
117
117
|
//#region src/exchange.d.ts
|
|
118
118
|
interface OperationMetadataMap {}
|
|
119
|
+
interface OperationResultMetadataMap {}
|
|
119
120
|
type OperationMetadata = { [K in keyof OperationMetadataMap]?: OperationMetadataMap[K] } & Record<string, unknown>;
|
|
120
121
|
type BaseOperation = {
|
|
121
122
|
key: string;
|
|
@@ -135,7 +136,7 @@ type OperationResult = {
|
|
|
135
136
|
data?: unknown;
|
|
136
137
|
errors?: readonly OperationError[];
|
|
137
138
|
extensions?: Record<string, unknown>;
|
|
138
|
-
|
|
139
|
+
metadata?: OperationResultMetadataMap & Record<string, unknown>;
|
|
139
140
|
};
|
|
140
141
|
type ExchangeInput<TMeta extends SchemaMeta$1 = SchemaMeta$1> = {
|
|
141
142
|
forward: ExchangeIO;
|
|
@@ -243,6 +244,11 @@ declare module '@mearie/core' {
|
|
|
243
244
|
interface ExchangeExtensionMap {
|
|
244
245
|
cache: CacheOperations;
|
|
245
246
|
}
|
|
247
|
+
interface OperationResultMetadataMap {
|
|
248
|
+
cache?: {
|
|
249
|
+
stale: boolean;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
246
252
|
}
|
|
247
253
|
type CacheOptions = {
|
|
248
254
|
fetchPolicy?: 'cache-first' | 'cache-and-network' | 'network-only' | 'cache-only';
|
|
@@ -345,4 +351,4 @@ declare class RequiredFieldError extends Error {
|
|
|
345
351
|
*/
|
|
346
352
|
declare const stringify: (value: unknown) => string;
|
|
347
353
|
//#endregion
|
|
348
|
-
export { AggregatedError, type Artifact, type ArtifactKind, type CacheOptions, type CacheSnapshot, Client, type ClientOptions, type DataOf, type Exchange, ExchangeError, type ExchangeErrorExtensionsMap, type ExchangeExtensionMap, type ExchangeIO, type ExchangeResult, type FragmentOptions, type FragmentRefs, GraphQLError, type HttpOptions, type MutationOptions, type Operation, type OperationError, type OperationMetadata, type OperationMetadataMap, type OperationResult, type QueryOptions, type RequiredAction, RequiredFieldError, type RetryOptions, type SchemaMeta, type SubscriptionClient, type SubscriptionExchangeOptions, type SubscriptionOptions, type VariablesOf, cacheExchange, createClient, dedupExchange, fragmentExchange, httpExchange, isAggregatedError, isExchangeError, isGraphQLError, requiredExchange, retryExchange, stringify, subscriptionExchange };
|
|
354
|
+
export { AggregatedError, type Artifact, type ArtifactKind, type CacheOptions, type CacheSnapshot, Client, type ClientOptions, type DataOf, type Exchange, ExchangeError, type ExchangeErrorExtensionsMap, type ExchangeExtensionMap, type ExchangeIO, type ExchangeResult, type FragmentOptions, type FragmentRefs, GraphQLError, type HttpOptions, type MutationOptions, type Operation, type OperationError, type OperationMetadata, type OperationMetadataMap, type OperationResult, type OperationResultMetadataMap, type QueryOptions, type RequiredAction, RequiredFieldError, type RetryOptions, type SchemaMeta, type SubscriptionClient, type SubscriptionExchangeOptions, type SubscriptionOptions, type VariablesOf, cacheExchange, createClient, dedupExchange, fragmentExchange, httpExchange, isAggregatedError, isExchangeError, isGraphQLError, requiredExchange, retryExchange, stringify, subscriptionExchange };
|
package/dist/index.mjs
CHANGED
|
@@ -593,8 +593,10 @@ const denormalize = (selections, storage, value, variables, accessor) => {
|
|
|
593
593
|
const value = selection.selections ? denormalizeField(null, selection.selections, fieldValue) : fieldValue;
|
|
594
594
|
if (name in fields) mergeFields(fields, { [name]: value });
|
|
595
595
|
else fields[name] = value;
|
|
596
|
-
} else if (selection.kind === "FragmentSpread") if (storageKey !== null && storageKey !== RootFieldKey)
|
|
597
|
-
|
|
596
|
+
} else if (selection.kind === "FragmentSpread") if (storageKey !== null && storageKey !== RootFieldKey) {
|
|
597
|
+
fields[FragmentRefKey] = storageKey;
|
|
598
|
+
if (accessor) denormalize(selection.selections, storage, { [EntityLinkKey]: storageKey }, variables, accessor);
|
|
599
|
+
} else mergeFields(fields, denormalizeField(storageKey, selection.selections, value));
|
|
598
600
|
else if (selection.kind === "InlineFragment" && selection.on === data[typenameFieldKey]) mergeFields(fields, denormalizeField(storageKey, selection.selections, value));
|
|
599
601
|
return fields;
|
|
600
602
|
};
|
|
@@ -615,6 +617,7 @@ var Cache = class {
|
|
|
615
617
|
#storage = { [RootFieldKey]: {} };
|
|
616
618
|
#subscriptions = /* @__PURE__ */ new Map();
|
|
617
619
|
#memo = /* @__PURE__ */ new Map();
|
|
620
|
+
#stale = /* @__PURE__ */ new Set();
|
|
618
621
|
constructor(schemaMetadata) {
|
|
619
622
|
this.#schemaMeta = schemaMetadata;
|
|
620
623
|
}
|
|
@@ -627,12 +630,14 @@ var Cache = class {
|
|
|
627
630
|
writeQuery(artifact, variables, data) {
|
|
628
631
|
const dependencies = /* @__PURE__ */ new Set();
|
|
629
632
|
const subscriptions = /* @__PURE__ */ new Set();
|
|
633
|
+
const entityStaleCleared = /* @__PURE__ */ new Set();
|
|
630
634
|
normalize(this.#schemaMeta, artifact.selections, this.#storage, data, variables, (storageKey, fieldKey, oldValue, newValue) => {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
+
const depKey = makeDependencyKey(storageKey, fieldKey);
|
|
636
|
+
if (this.#stale.delete(depKey)) dependencies.add(depKey);
|
|
637
|
+
if (!entityStaleCleared.has(storageKey) && this.#stale.delete(storageKey)) entityStaleCleared.add(storageKey);
|
|
638
|
+
if (oldValue !== newValue) dependencies.add(depKey);
|
|
635
639
|
});
|
|
640
|
+
for (const entityKey of entityStaleCleared) this.#collectSubscriptions(entityKey, void 0, subscriptions);
|
|
636
641
|
for (const dependency of dependencies) {
|
|
637
642
|
const ss = this.#subscriptions.get(dependency);
|
|
638
643
|
if (ss) for (const s of ss) subscriptions.add(s);
|
|
@@ -647,13 +652,22 @@ var Cache = class {
|
|
|
647
652
|
* @returns Denormalized query result or null if not found.
|
|
648
653
|
*/
|
|
649
654
|
readQuery(artifact, variables) {
|
|
650
|
-
|
|
651
|
-
|
|
655
|
+
let stale = false;
|
|
656
|
+
const { data, partial } = denormalize(artifact.selections, this.#storage, this.#storage[RootFieldKey], variables, (storageKey, fieldKey) => {
|
|
657
|
+
if (this.#stale.has(storageKey) || this.#stale.has(makeDependencyKey(storageKey, fieldKey))) stale = true;
|
|
658
|
+
});
|
|
659
|
+
if (partial) return {
|
|
660
|
+
data: null,
|
|
661
|
+
stale: false
|
|
662
|
+
};
|
|
652
663
|
const key = makeMemoKey("query", artifact.name, stringify(variables));
|
|
653
664
|
const prev = this.#memo.get(key);
|
|
654
665
|
const result = prev === void 0 ? data : replaceEqualDeep(prev, data);
|
|
655
666
|
this.#memo.set(key, result);
|
|
656
|
-
return
|
|
667
|
+
return {
|
|
668
|
+
data: result,
|
|
669
|
+
stale
|
|
670
|
+
};
|
|
657
671
|
}
|
|
658
672
|
/**
|
|
659
673
|
* Subscribes to cache invalidations for a specific query.
|
|
@@ -679,14 +693,26 @@ var Cache = class {
|
|
|
679
693
|
*/
|
|
680
694
|
readFragment(artifact, fragmentRef) {
|
|
681
695
|
const entityKey = fragmentRef[FragmentRefKey];
|
|
682
|
-
if (!this.#storage[entityKey]) return
|
|
683
|
-
|
|
684
|
-
|
|
696
|
+
if (!this.#storage[entityKey]) return {
|
|
697
|
+
data: null,
|
|
698
|
+
stale: false
|
|
699
|
+
};
|
|
700
|
+
let stale = false;
|
|
701
|
+
const { data, partial } = denormalize(artifact.selections, this.#storage, { [EntityLinkKey]: entityKey }, {}, (storageKey, fieldKey) => {
|
|
702
|
+
if (this.#stale.has(storageKey) || this.#stale.has(makeDependencyKey(storageKey, fieldKey))) stale = true;
|
|
703
|
+
});
|
|
704
|
+
if (partial) return {
|
|
705
|
+
data: null,
|
|
706
|
+
stale: false
|
|
707
|
+
};
|
|
685
708
|
const key = makeMemoKey("fragment", artifact.name, entityKey);
|
|
686
709
|
const prev = this.#memo.get(key);
|
|
687
710
|
const result = prev === void 0 ? data : replaceEqualDeep(prev, data);
|
|
688
711
|
this.#memo.set(key, result);
|
|
689
|
-
return
|
|
712
|
+
return {
|
|
713
|
+
data: result,
|
|
714
|
+
stale
|
|
715
|
+
};
|
|
690
716
|
}
|
|
691
717
|
subscribeFragment(artifact, fragmentRef, listener) {
|
|
692
718
|
const entityKey = fragmentRef[FragmentRefKey];
|
|
@@ -699,17 +725,25 @@ var Cache = class {
|
|
|
699
725
|
}
|
|
700
726
|
readFragments(artifact, fragmentRefs) {
|
|
701
727
|
const results = [];
|
|
728
|
+
let stale = false;
|
|
702
729
|
for (const ref of fragmentRefs) {
|
|
703
|
-
const
|
|
704
|
-
if (data === null) return
|
|
705
|
-
|
|
730
|
+
const result = this.readFragment(artifact, ref);
|
|
731
|
+
if (result.data === null) return {
|
|
732
|
+
data: null,
|
|
733
|
+
stale: false
|
|
734
|
+
};
|
|
735
|
+
if (result.stale) stale = true;
|
|
736
|
+
results.push(result.data);
|
|
706
737
|
}
|
|
707
738
|
const entityKeys = fragmentRefs.map((ref) => ref[FragmentRefKey]);
|
|
708
739
|
const key = makeMemoKey("fragments", artifact.name, entityKeys.join(","));
|
|
709
740
|
const prev = this.#memo.get(key);
|
|
710
741
|
const result = prev === void 0 ? results : replaceEqualDeep(prev, results);
|
|
711
742
|
this.#memo.set(key, result);
|
|
712
|
-
return
|
|
743
|
+
return {
|
|
744
|
+
data: result,
|
|
745
|
+
stale
|
|
746
|
+
};
|
|
713
747
|
}
|
|
714
748
|
subscribeFragments(artifact, fragmentRefs, listener) {
|
|
715
749
|
const dependencies = /* @__PURE__ */ new Set();
|
|
@@ -729,20 +763,21 @@ var Cache = class {
|
|
|
729
763
|
const subscriptions = /* @__PURE__ */ new Set();
|
|
730
764
|
for (const target of targets) if (target.__typename === "Query") if ("field" in target) {
|
|
731
765
|
const fieldKey = makeFieldKeyFromArgs(target.field, target.args);
|
|
732
|
-
|
|
766
|
+
const depKey = makeDependencyKey(RootFieldKey, fieldKey);
|
|
767
|
+
this.#stale.add(depKey);
|
|
733
768
|
this.#collectSubscriptions(RootFieldKey, fieldKey, subscriptions);
|
|
734
769
|
} else {
|
|
735
|
-
this.#
|
|
770
|
+
this.#stale.add(RootFieldKey);
|
|
736
771
|
this.#collectSubscriptions(RootFieldKey, void 0, subscriptions);
|
|
737
772
|
}
|
|
738
773
|
else if ("id" in target) {
|
|
739
774
|
const entityKey = resolveEntityKey(target.__typename, target.id, this.#schemaMeta.entities[target.__typename]?.keyFields);
|
|
740
775
|
if ("field" in target) {
|
|
741
776
|
const fieldKey = makeFieldKeyFromArgs(target.field, target.args);
|
|
742
|
-
|
|
777
|
+
this.#stale.add(makeDependencyKey(entityKey, fieldKey));
|
|
743
778
|
this.#collectSubscriptions(entityKey, fieldKey, subscriptions);
|
|
744
779
|
} else {
|
|
745
|
-
|
|
780
|
+
this.#stale.add(entityKey);
|
|
746
781
|
this.#collectSubscriptions(entityKey, void 0, subscriptions);
|
|
747
782
|
}
|
|
748
783
|
} else {
|
|
@@ -751,10 +786,10 @@ var Cache = class {
|
|
|
751
786
|
const entityKey = key;
|
|
752
787
|
if ("field" in target) {
|
|
753
788
|
const fieldKey = makeFieldKeyFromArgs(target.field, target.args);
|
|
754
|
-
|
|
789
|
+
this.#stale.add(makeDependencyKey(entityKey, fieldKey));
|
|
755
790
|
this.#collectSubscriptions(entityKey, fieldKey, subscriptions);
|
|
756
791
|
} else {
|
|
757
|
-
|
|
792
|
+
this.#stale.add(entityKey);
|
|
758
793
|
this.#collectSubscriptions(entityKey, void 0, subscriptions);
|
|
759
794
|
}
|
|
760
795
|
}
|
|
@@ -813,6 +848,7 @@ var Cache = class {
|
|
|
813
848
|
this.#storage = { [RootFieldKey]: {} };
|
|
814
849
|
this.#subscriptions.clear();
|
|
815
850
|
this.#memo.clear();
|
|
851
|
+
this.#stale.clear();
|
|
816
852
|
}
|
|
817
853
|
};
|
|
818
854
|
|
|
@@ -856,9 +892,10 @@ const cacheExchange = (options = {}) => {
|
|
|
856
892
|
return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readFragments(op.artifact, fragmentRef), () => cache.subscribeFragments(op.artifact, fragmentRef, async () => {
|
|
857
893
|
await Promise.resolve();
|
|
858
894
|
trigger.next();
|
|
859
|
-
}))), takeUntil(teardown$), map((data) => ({
|
|
895
|
+
}))), takeUntil(teardown$), map(({ data, stale }) => ({
|
|
860
896
|
operation: op,
|
|
861
897
|
data,
|
|
898
|
+
...stale && { metadata: { cache: { stale: true } } },
|
|
862
899
|
errors: []
|
|
863
900
|
})));
|
|
864
901
|
}
|
|
@@ -872,9 +909,10 @@ const cacheExchange = (options = {}) => {
|
|
|
872
909
|
return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readFragment(op.artifact, fragmentRef), () => cache.subscribeFragment(op.artifact, fragmentRef, async () => {
|
|
873
910
|
await Promise.resolve();
|
|
874
911
|
trigger.next();
|
|
875
|
-
}))), takeUntil(teardown$), map((data) => ({
|
|
912
|
+
}))), takeUntil(teardown$), map(({ data, stale }) => ({
|
|
876
913
|
operation: op,
|
|
877
914
|
data,
|
|
915
|
+
...stale && { metadata: { cache: { stale: true } } },
|
|
878
916
|
errors: []
|
|
879
917
|
})));
|
|
880
918
|
}));
|
|
@@ -888,8 +926,8 @@ const cacheExchange = (options = {}) => {
|
|
|
888
926
|
return pipe(merge(fromValue(void 0), trigger.source), switchMap(() => fromSubscription(() => cache.readQuery(op.artifact, op.variables), () => cache.subscribeQuery(op.artifact, op.variables, async () => {
|
|
889
927
|
await Promise.resolve();
|
|
890
928
|
trigger.next();
|
|
891
|
-
}))), takeUntil(teardown$), mergeMap((data) => {
|
|
892
|
-
if (data !== null) {
|
|
929
|
+
}))), takeUntil(teardown$), mergeMap(({ data, stale }) => {
|
|
930
|
+
if (data !== null && !stale) {
|
|
893
931
|
hasData = true;
|
|
894
932
|
return fromValue({
|
|
895
933
|
operation: op,
|
|
@@ -897,6 +935,16 @@ const cacheExchange = (options = {}) => {
|
|
|
897
935
|
errors: []
|
|
898
936
|
});
|
|
899
937
|
}
|
|
938
|
+
if (data !== null && stale) {
|
|
939
|
+
hasData = true;
|
|
940
|
+
refetch$.next(op);
|
|
941
|
+
return fromValue({
|
|
942
|
+
operation: op,
|
|
943
|
+
data,
|
|
944
|
+
metadata: { cache: { stale: true } },
|
|
945
|
+
errors: []
|
|
946
|
+
});
|
|
947
|
+
}
|
|
900
948
|
if (hasData) {
|
|
901
949
|
refetch$.next(op);
|
|
902
950
|
return empty();
|
|
@@ -909,8 +957,8 @@ const cacheExchange = (options = {}) => {
|
|
|
909
957
|
return empty();
|
|
910
958
|
}));
|
|
911
959
|
}), filter(() => fetchPolicy === "cache-only" || fetchPolicy === "cache-and-network" || fetchPolicy === "cache-first")), pipe(merge(nonCache$, pipe(query$, filter((op) => {
|
|
912
|
-
const
|
|
913
|
-
return fetchPolicy === "cache-and-network" ||
|
|
960
|
+
const { data } = cache.readQuery(op.artifact, op.variables);
|
|
961
|
+
return fetchPolicy === "cache-and-network" || data === null;
|
|
914
962
|
})), pipe(ops$, filter((op) => op.variant === "teardown")), refetch$.source), forward, tap((result) => {
|
|
915
963
|
if (result.operation.variant === "request" && result.data) cache.writeQuery(result.operation.artifact, result.operation.variables, result.data);
|
|
916
964
|
}), filter((result) => result.operation.variant !== "request" || result.operation.artifact.kind !== "query" || fetchPolicy === "network-only" || !!(result.errors && result.errors.length > 0))));
|
package/package.json
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearie/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Type-safe, zero-overhead GraphQL client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
7
7
|
"graphql-client",
|
|
8
8
|
"typescript",
|
|
9
|
+
"type-safe",
|
|
9
10
|
"codegen",
|
|
10
|
-
"cache"
|
|
11
|
+
"cache",
|
|
12
|
+
"normalized-cache",
|
|
13
|
+
"react",
|
|
14
|
+
"vue",
|
|
15
|
+
"svelte",
|
|
16
|
+
"solid",
|
|
17
|
+
"vite"
|
|
11
18
|
],
|
|
12
|
-
"homepage": "https://
|
|
19
|
+
"homepage": "https://mearie.dev/",
|
|
13
20
|
"bugs": {
|
|
14
21
|
"url": "https://github.com/devunt/mearie/issues"
|
|
15
22
|
},
|
|
@@ -55,7 +62,7 @@
|
|
|
55
62
|
"README.md"
|
|
56
63
|
],
|
|
57
64
|
"dependencies": {
|
|
58
|
-
"@mearie/shared": "0.2.
|
|
65
|
+
"@mearie/shared": "0.2.2"
|
|
59
66
|
},
|
|
60
67
|
"devDependencies": {
|
|
61
68
|
"tsdown": "^0.20.3",
|