@nkzw/fate 1.0.3 → 1.2.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/README.md +1220 -147
- package/docs/api/classes/FateClient.md +134 -40
- package/docs/api/functions/clientRoot.md +1 -1
- package/docs/api/functions/createClient.md +8 -4
- package/docs/api/functions/createGraphQLTransport.md +21 -0
- package/docs/api/functions/createHTTPTransport.md +1 -1
- package/docs/api/functions/createTRPCTransport.md +1 -1
- package/docs/api/functions/defer.md +21 -0
- package/docs/api/functions/getDeferredMetadata.md +21 -0
- package/docs/api/functions/getListEntries.md +1 -1
- package/docs/api/functions/getSelectionPlan.md +1 -1
- package/docs/api/functions/graphqlMutation.md +39 -0
- package/docs/api/functions/isDeferred.md +15 -0
- package/docs/api/functions/isRecord.md +1 -1
- package/docs/api/functions/isViewTag.md +1 -1
- package/docs/api/functions/liveConnectionTopic.md +1 -1
- package/docs/api/functions/liveEntityTopic.md +1 -1
- package/docs/api/functions/liveGlobalConnectionTopic.md +1 -1
- package/docs/api/functions/mutation.md +1 -1
- package/docs/api/functions/toEntityId.md +1 -1
- package/docs/api/functions/view.md +1 -1
- package/docs/api/index.md +19 -0
- package/docs/api/interfaces/FateThenable.md +3 -3
- package/docs/api/interfaces/Transport.md +7 -7
- package/docs/api/server/classes/FateRequestError.md +5 -5
- package/docs/api/server/drizzle/functions/createDrizzleFate.md +1 -1
- package/docs/api/server/drizzle/functions/createDrizzleSourceAdapter.md +1 -1
- package/docs/api/server/drizzle/functions/createDrizzleSourceRegistry.md +1 -1
- package/docs/api/server/drizzle/type-aliases/DrizzleManyToManyConfig.md +1 -1
- package/docs/api/server/drizzle/type-aliases/DrizzleManyToManyInput.md +1 -1
- package/docs/api/server/drizzle/type-aliases/DrizzleQueryExtra.md +3 -3
- package/docs/api/server/drizzle/type-aliases/DrizzleSourceAdapter.md +9 -9
- package/docs/api/server/drizzle/type-aliases/DrizzleViewConfig.md +1 -1
- package/docs/api/server/functions/bindSourceProcedures.md +1 -1
- package/docs/api/server/functions/computed.md +1 -1
- package/docs/api/server/functions/count.md +1 -1
- package/docs/api/server/functions/createFateFetchHandler.md +1 -1
- package/docs/api/server/functions/createFateServer.md +1 -1
- package/docs/api/server/functions/createHonoFateHandler.md +1 -1
- package/docs/api/server/functions/createLiveEventBus.md +1 -1
- package/docs/api/server/functions/createNestedSourcePlan.md +1 -1
- package/docs/api/server/functions/createResolver.md +1 -1
- package/docs/api/server/functions/createSourcePlan.md +1 -1
- package/docs/api/server/functions/createSourceProcedures.md +1 -1
- package/docs/api/server/functions/dataView.md +1 -1
- package/docs/api/server/functions/field.md +1 -1
- package/docs/api/server/functions/getNestedSelection.md +1 -1
- package/docs/api/server/functions/getScopedArgs.md +1 -1
- package/docs/api/server/functions/hasNestedSelection.md +1 -1
- package/docs/api/server/functions/list.md +1 -1
- package/docs/api/server/functions/refetchSourceById.md +1 -1
- package/docs/api/server/functions/resolveSourceById.md +1 -1
- package/docs/api/server/functions/resolveSourceByIds.md +1 -1
- package/docs/api/server/functions/resolveSourceConnection.md +1 -1
- package/docs/api/server/functions/resolver.md +1 -1
- package/docs/api/server/functions/toPrismaSelect.md +1 -1
- package/docs/api/server/functions/withConnection.md +1 -1
- package/docs/api/server/prisma/functions/createPrismaFate.md +1 -1
- package/docs/api/server/prisma/functions/createPrismaSourceAdapter.md +1 -1
- package/docs/api/server/prisma/functions/createPrismaSourceRegistry.md +1 -1
- package/docs/api/server/prisma/functions/prismaConnectionArgs.md +1 -1
- package/docs/api/server/prisma/type-aliases/PrismaDelegate.md +4 -4
- package/docs/api/server/prisma/type-aliases/PrismaQueryExtra.md +1 -1
- package/docs/api/server/prisma/type-aliases/PrismaSourceAdapter.md +9 -9
- package/docs/api/server/prisma/type-aliases/PrismaViewConfig.md +1 -1
- package/docs/api/server/type-aliases/ComputedField.md +5 -5
- package/docs/api/server/type-aliases/ComputedSelection.md +1 -1
- package/docs/api/server/type-aliases/ConnectionItem.md +3 -3
- package/docs/api/server/type-aliases/ConnectionPagination.md +5 -5
- package/docs/api/server/type-aliases/ConnectionResult.md +3 -3
- package/docs/api/server/type-aliases/CountSelection.md +4 -4
- package/docs/api/server/type-aliases/CountWhere.md +1 -1
- package/docs/api/server/type-aliases/DataViewListOptions.md +2 -2
- package/docs/api/server/type-aliases/DataViewOrderBy.md +1 -1
- package/docs/api/server/type-aliases/DataViewOrderDirection.md +1 -1
- package/docs/api/server/type-aliases/DataViewResult.md +1 -1
- package/docs/api/server/type-aliases/Entity.md +1 -1
- package/docs/api/server/type-aliases/FateServer.md +5 -5
- package/docs/api/server/type-aliases/FateServerManifest.md +1 -1
- package/docs/api/server/type-aliases/FieldSelection.md +3 -3
- package/docs/api/server/type-aliases/LiveConnectionEventType.md +1 -1
- package/docs/api/server/type-aliases/LiveConnectionSourceEvent.md +1 -1
- package/docs/api/server/type-aliases/LiveEventBus.md +1 -1
- package/docs/api/server/type-aliases/LiveEventType.md +1 -1
- package/docs/api/server/type-aliases/LiveSourceEvent.md +1 -1
- package/docs/api/server/type-aliases/NativeFateAPI.md +4 -4
- package/docs/api/server/type-aliases/OrderDirection.md +1 -1
- package/docs/api/server/type-aliases/SourceConfig.md +6 -6
- package/docs/api/server/type-aliases/SourceDefinition.md +6 -6
- package/docs/api/server/type-aliases/SourceOrder.md +1 -1
- package/docs/api/server/type-aliases/SourceOrderField.md +3 -3
- package/docs/api/server/type-aliases/SourcePlan.md +1 -1
- package/docs/api/server/type-aliases/SourcePlanNode.md +1 -1
- package/docs/api/server/type-aliases/SourceRegistry.md +1 -1
- package/docs/api/server/type-aliases/SourceRelation.md +1 -1
- package/docs/api/server/type-aliases/SourceRelationConfig.md +1 -1
- package/docs/api/server/variables/byIdInput.md +1 -1
- package/docs/api/server/variables/connectionArgs.md +1 -1
- package/docs/api/type-aliases/ConnectionMetadata.md +1 -1
- package/docs/api/type-aliases/ConnectionRef.md +1 -1
- package/docs/api/type-aliases/Deferred.md +19 -0
- package/docs/api/type-aliases/DeferredMetadata.md +7 -0
- package/docs/api/type-aliases/DeferredSelection.md +13 -0
- package/docs/api/type-aliases/DeferredSnapshot.md +11 -0
- package/docs/api/type-aliases/Entity.md +2 -2
- package/docs/api/type-aliases/EntityId.md +1 -1
- package/docs/api/type-aliases/FateDehydratedState.md +11 -0
- package/docs/api/type-aliases/FateLiveConnectionEvent.md +1 -1
- package/docs/api/type-aliases/FateLiveEvent.md +1 -1
- package/docs/api/type-aliases/FateMutations.md +1 -1
- package/docs/api/type-aliases/FateOperation.md +1 -1
- package/docs/api/type-aliases/FateProtocolRequest.md +1 -1
- package/docs/api/type-aliases/FateProtocolResponse.md +1 -1
- package/docs/api/type-aliases/FateRecord.md +1 -1
- package/docs/api/type-aliases/FateRoots.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationDefinition.md +19 -0
- package/docs/api/type-aliases/GraphQLMutationInput.md +11 -0
- package/docs/api/type-aliases/GraphQLMutationMap.md +11 -0
- package/docs/api/type-aliases/GraphQLMutationOutput.md +11 -0
- package/docs/api/type-aliases/GraphQLTransportOptions.md +119 -0
- package/docs/api/type-aliases/HydrateOptions.md +7 -0
- package/docs/api/type-aliases/HydrationLimits.md +7 -0
- package/docs/api/type-aliases/InferFateAPI.md +1 -1
- package/docs/api/type-aliases/List.md +1 -1
- package/docs/api/type-aliases/ListEntry.md +1 -1
- package/docs/api/type-aliases/ListItem.md +1 -1
- package/docs/api/type-aliases/Mask.md +1 -1
- package/docs/api/type-aliases/MutationDefinition.md +1 -1
- package/docs/api/type-aliases/MutationEntity.md +1 -1
- package/docs/api/type-aliases/MutationIdentifier.md +1 -1
- package/docs/api/type-aliases/MutationInput.md +1 -1
- package/docs/api/type-aliases/MutationResult.md +1 -1
- package/docs/api/type-aliases/NodesItem.md +1 -1
- package/docs/api/type-aliases/Pagination.md +5 -5
- package/docs/api/type-aliases/Request.md +1 -1
- package/docs/api/type-aliases/RequestMode.md +1 -1
- package/docs/api/type-aliases/RequestOptions.md +1 -1
- package/docs/api/type-aliases/RequestResult.md +1 -1
- package/docs/api/type-aliases/Selection.md +1 -1
- package/docs/api/type-aliases/Snapshot.md +1 -1
- package/docs/api/type-aliases/TypeConfig.md +4 -4
- package/docs/api/type-aliases/View.md +1 -1
- package/docs/api/type-aliases/ViewData.md +1 -1
- package/docs/api/type-aliases/ViewEntity.md +1 -1
- package/docs/api/type-aliases/ViewEntityName.md +1 -1
- package/docs/api/type-aliases/ViewRef.md +1 -1
- package/docs/api/type-aliases/ViewSelection.md +1 -1
- package/docs/api/type-aliases/ViewSnapshot.md +1 -1
- package/docs/api/type-aliases/ViewTag.md +1 -1
- package/docs/api/variables/ConnectionTag.md +1 -1
- package/docs/api/variables/DeferTag.md +7 -0
- package/docs/api/variables/DeferredTag.md +7 -0
- package/docs/guide/actions.md +7 -7
- package/docs/guide/deferred-views.md +62 -0
- package/docs/guide/getting-started.md +21 -3
- package/docs/guide/requests.md +52 -0
- package/docs/guide/vue.md +256 -0
- package/docs/index.md +1 -0
- package/docs/integrations/graphql.md +345 -0
- package/docs/{guide/server-integration.md → integrations/server.md} +94 -7
- package/docs/{guide/void-integration.md → integrations/void.md} +94 -12
- package/lib/{executor-BqwHdN3n.d.mts → executor-Dh-MLUXc.d.mts} +1 -1
- package/lib/graphqlTransport-B3cbhvJX.d.mts +74 -0
- package/lib/graphqlTransport-IFgmY-Qq.mjs +499 -0
- package/lib/graphqlTransport.d.mts +2 -0
- package/lib/graphqlTransport.mjs +2 -0
- package/lib/index.d.mts +10 -4
- package/lib/index.mjs +619 -91
- package/lib/{list-4wRNSVgI.d.mts → list-BrL6PD_6.d.mts} +1 -1
- package/lib/list.d.mts +1 -1
- package/lib/list.mjs +1 -1
- package/lib/{liveTopics-DzNtJaBD.mjs → liveTopics-JIDh3t3P.mjs} +1 -1
- package/lib/{record-CirbfZWX.d.mts → record-BkvZwvFk.d.mts} +2 -2
- package/lib/server/drizzle.d.mts +6 -8
- package/lib/server/drizzle.mjs +2 -2
- package/lib/server/prisma.d.mts +6 -8
- package/lib/server/prisma.mjs +2 -2
- package/lib/server.d.mts +3 -3
- package/lib/server.mjs +3 -3
- package/lib/{sourceRouter-C22uFcZg.mjs → sourceRouter-N9Vp0Cj6.mjs} +1 -1
- package/lib/{types-Dz46PXr3.d.mts → transport-BjGm__3t.d.mts} +246 -145
- package/lib/vite.d.mts +2 -2
- package/lib/vite.mjs +150 -3
- package/package.json +11 -3
- /package/lib/{list-C_mi6GnE.mjs → list-_6fXYLtJ.mjs} +0 -0
- /package/lib/{record-AeZJC9fd.mjs → record-B07VwXd-.mjs} +0 -0
package/lib/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { t as isRecord } from "./record-
|
|
2
|
-
import { a as errorCodeFromStatus, c as cloneArgs, d as hashArgs, f as paginationArgKeys, i as FateRequestError, l as combineArgsPayload, m as scopeArgsPayload, n as liveEntityTopic, p as resolvedArgsFromPlan, r as liveGlobalConnectionTopic, s as applyArgsPayloadToPlan, t as liveConnectionTopic, u as filterConnectionArgs } from "./liveTopics-
|
|
3
|
-
import { t as getListEntries } from "./list-
|
|
1
|
+
import { t as isRecord } from "./record-B07VwXd-.mjs";
|
|
2
|
+
import { a as errorCodeFromStatus, c as cloneArgs, d as hashArgs, f as paginationArgKeys, i as FateRequestError, l as combineArgsPayload, m as scopeArgsPayload, n as liveEntityTopic, p as resolvedArgsFromPlan, r as liveGlobalConnectionTopic, s as applyArgsPayloadToPlan, t as liveConnectionTopic, u as filterConnectionArgs } from "./liveTopics-JIDh3t3P.mjs";
|
|
3
|
+
import { t as getListEntries } from "./list-_6fXYLtJ.mjs";
|
|
4
|
+
import { n as graphqlMutation, t as createGraphQLTransport } from "./graphqlTransport-IFgmY-Qq.mjs";
|
|
4
5
|
//#region src/cache.ts
|
|
5
6
|
var ViewDataCache = class {
|
|
6
7
|
constructor() {
|
|
@@ -8,6 +9,11 @@ var ViewDataCache = class {
|
|
|
8
9
|
this.rootDependencies = /* @__PURE__ */ new Map();
|
|
9
10
|
this.dependencyIndex = /* @__PURE__ */ new Map();
|
|
10
11
|
}
|
|
12
|
+
clear() {
|
|
13
|
+
this.cache.clear();
|
|
14
|
+
this.rootDependencies.clear();
|
|
15
|
+
this.dependencyIndex.clear();
|
|
16
|
+
}
|
|
11
17
|
get(entityId, view, ref) {
|
|
12
18
|
return this.cache.get(entityId)?.get(view)?.get(ref) ?? null;
|
|
13
19
|
}
|
|
@@ -63,6 +69,288 @@ var ViewDataCache = class {
|
|
|
63
69
|
}
|
|
64
70
|
};
|
|
65
71
|
//#endregion
|
|
72
|
+
//#region src/types.ts
|
|
73
|
+
/** Internal marker added to objects that represent a view payload. */
|
|
74
|
+
const ViewKind = Symbol("__fate__view");
|
|
75
|
+
/** Symbol used to attach the set of view tags that were spread into a ref or masked record. */
|
|
76
|
+
const ViewsTag = Symbol("__fate__views");
|
|
77
|
+
/** Symbol used to brand a value as a node reference inside the cache. */
|
|
78
|
+
const NodeRefTag = Symbol("__fate__node-ref");
|
|
79
|
+
/** Symbol attached to connection results so pagination helpers can find their metadata. */
|
|
80
|
+
const ConnectionTag = Symbol("__fate__connection");
|
|
81
|
+
/** Symbol used to mark a selection as deferred. */
|
|
82
|
+
const DeferTag = Symbol("__fate__defer");
|
|
83
|
+
/** Symbol attached to deferred values returned from masked views. */
|
|
84
|
+
const DeferredTag = Symbol("__fate__deferred");
|
|
85
|
+
const viewTag = "__fate-view__";
|
|
86
|
+
/** Generates a stable view tag for a view definition. */
|
|
87
|
+
function getViewTag(id) {
|
|
88
|
+
return `${viewTag}${id}`;
|
|
89
|
+
}
|
|
90
|
+
/** Determines whether a property key is a fate view tag. */
|
|
91
|
+
function isViewTag(key) {
|
|
92
|
+
return key.startsWith(viewTag);
|
|
93
|
+
}
|
|
94
|
+
/** Indicates whether a request item represents an explicit node ID. */
|
|
95
|
+
function isNodeItem(item) {
|
|
96
|
+
return "id" in item;
|
|
97
|
+
}
|
|
98
|
+
/** Indicates whether a request item represents explicit node IDs. */
|
|
99
|
+
function isNodesItem(item) {
|
|
100
|
+
return "ids" in item;
|
|
101
|
+
}
|
|
102
|
+
/** Indicates whether a request item represents a root query. */
|
|
103
|
+
function isQueryItem(item) {
|
|
104
|
+
return "view" in item && !("id" in item) && !("ids" in item);
|
|
105
|
+
}
|
|
106
|
+
/** Brand used on root definitions to mark their identity in the d.ts output. */
|
|
107
|
+
const RootKind = "__fate__root";
|
|
108
|
+
/** Brand used on mutation definitions to mark their identity in the d.ts output. */
|
|
109
|
+
const MutationKind = "__fate__mutation";
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/defer.ts
|
|
112
|
+
function defer(selection) {
|
|
113
|
+
return Object.freeze({ [DeferTag]: selection });
|
|
114
|
+
}
|
|
115
|
+
const isDeferredSelection = (value) => typeof value === "object" && value !== null && DeferTag in value;
|
|
116
|
+
const getDeferredSelection = (selection) => selection[DeferTag];
|
|
117
|
+
const createDeferred = (metadata) => Object.freeze({ [DeferredTag]: metadata });
|
|
118
|
+
const isDeferred = (value) => typeof value === "object" && value !== null && DeferredTag in value;
|
|
119
|
+
const getDeferredMetadata = (deferred) => deferred[DeferredTag];
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/node-ref.ts
|
|
122
|
+
function createNodeRef(id) {
|
|
123
|
+
const ref = {};
|
|
124
|
+
Object.defineProperty(ref, NodeRefTag, {
|
|
125
|
+
configurable: false,
|
|
126
|
+
enumerable: false,
|
|
127
|
+
value: id,
|
|
128
|
+
writable: false
|
|
129
|
+
});
|
|
130
|
+
return Object.freeze(ref);
|
|
131
|
+
}
|
|
132
|
+
function isNodeRef(value) {
|
|
133
|
+
return !value || typeof value !== "object" ? false : NodeRefTag in value;
|
|
134
|
+
}
|
|
135
|
+
function getNodeRefId(ref) {
|
|
136
|
+
return ref[NodeRefTag];
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/hydration.ts
|
|
140
|
+
const maxDepth = 64;
|
|
141
|
+
const defaultHydrationLimits = {
|
|
142
|
+
maxCollectionLength: 1e5,
|
|
143
|
+
maxNodes: 25e4,
|
|
144
|
+
maxStringLength: 1e6
|
|
145
|
+
};
|
|
146
|
+
const resolveHydrationLimits = (limits) => {
|
|
147
|
+
const result = {
|
|
148
|
+
...defaultHydrationLimits,
|
|
149
|
+
...limits
|
|
150
|
+
};
|
|
151
|
+
for (const [key, value] of Object.entries(result)) if (!Number.isInteger(value) || value < 1) throw new Error(`fate: Hydration limit '${key}' must be a positive integer.`);
|
|
152
|
+
return result;
|
|
153
|
+
};
|
|
154
|
+
const error = (path, message) => /* @__PURE__ */ new Error(`fate: Cannot serialize hydration value at '${path}': ${message}`);
|
|
155
|
+
const isPlainObject = (value) => {
|
|
156
|
+
const prototype = Object.getPrototypeOf(value);
|
|
157
|
+
return prototype === Object.prototype || prototype === null;
|
|
158
|
+
};
|
|
159
|
+
const isDenseArray = (value) => {
|
|
160
|
+
for (let index = 0; index < value.length; index += 1) if (!Object.hasOwn(value, index)) return false;
|
|
161
|
+
return true;
|
|
162
|
+
};
|
|
163
|
+
const isDate$1 = (value) => Object.prototype.toString.call(value) === "[object Date]" && typeof value.getTime === "function" && typeof value.toISOString === "function";
|
|
164
|
+
const encodeHydrationValue = (value, limits) => {
|
|
165
|
+
const resolvedLimits = resolveHydrationLimits(limits);
|
|
166
|
+
const seen = /* @__PURE__ */ new Set();
|
|
167
|
+
let nodes = 0;
|
|
168
|
+
const encode = (entry, path, depth) => {
|
|
169
|
+
nodes += 1;
|
|
170
|
+
if (nodes > resolvedLimits.maxNodes) throw error(path, `Maximum node count of ${String(resolvedLimits.maxNodes)} exceeded.`);
|
|
171
|
+
if (depth > maxDepth) throw error(path, `Maximum depth of ${String(maxDepth)} exceeded.`);
|
|
172
|
+
if (entry === null) return ["null"];
|
|
173
|
+
const type = typeof entry;
|
|
174
|
+
if (typeof entry === "string" || typeof entry === "boolean") {
|
|
175
|
+
if (typeof entry === "string" && entry.length > resolvedLimits.maxStringLength) throw error(path, `Maximum string length of ${String(resolvedLimits.maxStringLength)} exceeded.`);
|
|
176
|
+
return [typeof entry, entry];
|
|
177
|
+
}
|
|
178
|
+
if (type === "number") {
|
|
179
|
+
if (Number.isNaN(entry)) return ["number", "NaN"];
|
|
180
|
+
if (entry === Infinity) return ["number", "Infinity"];
|
|
181
|
+
if (entry === -Infinity) return ["number", "-Infinity"];
|
|
182
|
+
if (Object.is(entry, -0)) return ["number", "-0"];
|
|
183
|
+
return ["number", entry];
|
|
184
|
+
}
|
|
185
|
+
if (type === "undefined") return ["undefined"];
|
|
186
|
+
if (type === "bigint") {
|
|
187
|
+
const bigint = String(entry);
|
|
188
|
+
if (bigint.length > resolvedLimits.maxStringLength) throw error(path, `Maximum string length of ${String(resolvedLimits.maxStringLength)} exceeded.`);
|
|
189
|
+
return ["bigint", bigint];
|
|
190
|
+
}
|
|
191
|
+
if (type === "function" || type === "symbol") throw error(path, `Received unsupported '${type}' value.`);
|
|
192
|
+
if (!entry || typeof entry !== "object") throw error(path, `Received unsupported '${type}' value.`);
|
|
193
|
+
if (isNodeRef(entry)) {
|
|
194
|
+
const id = getNodeRefId(entry);
|
|
195
|
+
if (id.length > resolvedLimits.maxStringLength) throw error(path, `Maximum string length of ${String(resolvedLimits.maxStringLength)} exceeded.`);
|
|
196
|
+
return ["ref", id];
|
|
197
|
+
}
|
|
198
|
+
if (isDate$1(entry)) {
|
|
199
|
+
if (Number.isNaN(entry.getTime())) throw error(path, `Received an invalid Date.`);
|
|
200
|
+
return ["date", entry.toISOString()];
|
|
201
|
+
}
|
|
202
|
+
if (seen.has(entry)) throw error(path, `Circular references are not supported.`);
|
|
203
|
+
seen.add(entry);
|
|
204
|
+
try {
|
|
205
|
+
if (Array.isArray(entry)) {
|
|
206
|
+
if (!isDenseArray(entry)) throw error(path, `Sparse arrays are not supported.`);
|
|
207
|
+
if (entry.length > resolvedLimits.maxCollectionLength) throw error(path, `Maximum collection length of ${String(resolvedLimits.maxCollectionLength)} exceeded.`);
|
|
208
|
+
return ["array", entry.map((item, index) => encode(item, `${path}[${String(index)}]`, depth + 1))];
|
|
209
|
+
}
|
|
210
|
+
if (!isPlainObject(entry)) throw error(path, `Received unsupported '${entry.constructor?.name ?? "object"}' value.`);
|
|
211
|
+
if (Object.getOwnPropertySymbols(entry).length > 0) throw error(path, `Received an unsupported symbol-keyed property.`);
|
|
212
|
+
const entries = Object.entries(entry);
|
|
213
|
+
if (entries.length > resolvedLimits.maxCollectionLength) throw error(path, `Maximum collection length of ${String(resolvedLimits.maxCollectionLength)} exceeded.`);
|
|
214
|
+
return ["object", entries.map(([key, item]) => {
|
|
215
|
+
if (key.length > resolvedLimits.maxStringLength) throw error(path, `Maximum string length of ${String(resolvedLimits.maxStringLength)} exceeded.`);
|
|
216
|
+
return [key, encode(item, `${path}.${key}`, depth + 1)];
|
|
217
|
+
})];
|
|
218
|
+
} finally {
|
|
219
|
+
seen.delete(entry);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
return encode(value, "data", 0);
|
|
223
|
+
};
|
|
224
|
+
const invalidPayload = (path) => /* @__PURE__ */ new Error(`fate: Invalid hydration payload at '${path}'.`);
|
|
225
|
+
const tagLengths = {
|
|
226
|
+
array: 2,
|
|
227
|
+
bigint: 2,
|
|
228
|
+
boolean: 2,
|
|
229
|
+
date: 2,
|
|
230
|
+
null: 1,
|
|
231
|
+
number: 2,
|
|
232
|
+
object: 2,
|
|
233
|
+
ref: 2,
|
|
234
|
+
string: 2,
|
|
235
|
+
undefined: 1
|
|
236
|
+
};
|
|
237
|
+
const decodeHydrationValue = (value, limits) => {
|
|
238
|
+
const resolvedLimits = resolveHydrationLimits(limits);
|
|
239
|
+
let nodes = 0;
|
|
240
|
+
const decode = (entry, path, depth) => {
|
|
241
|
+
nodes += 1;
|
|
242
|
+
if (nodes > resolvedLimits.maxNodes || depth > maxDepth || !Array.isArray(entry) || typeof entry[0] !== "string") throw invalidPayload(path);
|
|
243
|
+
const [tag, payload] = entry;
|
|
244
|
+
if (entry.length !== tagLengths[tag]) throw invalidPayload(path);
|
|
245
|
+
switch (tag) {
|
|
246
|
+
case "null": return null;
|
|
247
|
+
case "undefined": return;
|
|
248
|
+
case "string":
|
|
249
|
+
if (typeof payload !== "string" || payload.length > resolvedLimits.maxStringLength) throw invalidPayload(path);
|
|
250
|
+
return payload;
|
|
251
|
+
case "boolean":
|
|
252
|
+
if (typeof payload !== "boolean") throw invalidPayload(path);
|
|
253
|
+
return payload;
|
|
254
|
+
case "number":
|
|
255
|
+
if (typeof payload === "number") {
|
|
256
|
+
if (!Number.isFinite(payload) || Object.is(payload, -0)) throw invalidPayload(path);
|
|
257
|
+
return payload;
|
|
258
|
+
}
|
|
259
|
+
if (payload === "NaN") return NaN;
|
|
260
|
+
if (payload === "Infinity") return Infinity;
|
|
261
|
+
if (payload === "-Infinity") return -Infinity;
|
|
262
|
+
if (payload === "-0") return -0;
|
|
263
|
+
throw invalidPayload(path);
|
|
264
|
+
case "bigint":
|
|
265
|
+
if (typeof payload !== "string" || payload.length > resolvedLimits.maxStringLength) throw invalidPayload(path);
|
|
266
|
+
try {
|
|
267
|
+
const bigint = BigInt(payload);
|
|
268
|
+
if (String(bigint) !== payload) throw invalidPayload(path);
|
|
269
|
+
return bigint;
|
|
270
|
+
} catch {
|
|
271
|
+
throw invalidPayload(path);
|
|
272
|
+
}
|
|
273
|
+
case "date": {
|
|
274
|
+
if (typeof payload !== "string" || payload.length > resolvedLimits.maxStringLength) throw invalidPayload(path);
|
|
275
|
+
const date = new Date(payload);
|
|
276
|
+
if (Number.isNaN(date.getTime()) || date.toISOString() !== payload) throw invalidPayload(path);
|
|
277
|
+
return date;
|
|
278
|
+
}
|
|
279
|
+
case "ref":
|
|
280
|
+
if (typeof payload !== "string" || payload.length > resolvedLimits.maxStringLength) throw invalidPayload(path);
|
|
281
|
+
return createNodeRef(payload);
|
|
282
|
+
case "array":
|
|
283
|
+
if (!Array.isArray(payload) || !isDenseArray(payload) || payload.length > resolvedLimits.maxCollectionLength) throw invalidPayload(path);
|
|
284
|
+
return payload.map((item, index) => decode(item, `${path}[${String(index)}]`, depth + 1));
|
|
285
|
+
case "object": {
|
|
286
|
+
if (!Array.isArray(payload) || payload.length > resolvedLimits.maxCollectionLength) throw invalidPayload(path);
|
|
287
|
+
const result = Object.create(null);
|
|
288
|
+
for (const item of payload) {
|
|
289
|
+
if (!Array.isArray(item) || item.length !== 2 || typeof item[0] !== "string" || item[0].length > resolvedLimits.maxStringLength) throw invalidPayload(path);
|
|
290
|
+
if (item[0] in result) throw invalidPayload(path);
|
|
291
|
+
result[item[0]] = decode(item[1], `${path}.${item[0]}`, depth + 1);
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
default: throw invalidPayload(path);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
return decode(value, "data", 0);
|
|
299
|
+
};
|
|
300
|
+
const isRecord$1 = (value) => {
|
|
301
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || isNodeRef(value)) return false;
|
|
302
|
+
const prototype = Object.getPrototypeOf(value);
|
|
303
|
+
return prototype === Object.prototype || prototype === null;
|
|
304
|
+
};
|
|
305
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
306
|
+
const hasDuplicates = (values) => new Set(values).size !== values.length;
|
|
307
|
+
const isUniqueStringArray = (value) => isStringArray(value) && !hasDuplicates(value);
|
|
308
|
+
const hasOnlyKeys = (value, keys) => Object.keys(value).every((key) => keys.has(key));
|
|
309
|
+
const listKeys = new Set([
|
|
310
|
+
"backwardPageLimit",
|
|
311
|
+
"cursors",
|
|
312
|
+
"forwardPageLimit",
|
|
313
|
+
"ids",
|
|
314
|
+
"liveAfterIds",
|
|
315
|
+
"liveBeforeIds",
|
|
316
|
+
"pagination",
|
|
317
|
+
"pendingAfterIds",
|
|
318
|
+
"pendingBeforeIds"
|
|
319
|
+
]);
|
|
320
|
+
const paginationKeys = new Set([
|
|
321
|
+
"hasNext",
|
|
322
|
+
"hasPrevious",
|
|
323
|
+
"nextCursor",
|
|
324
|
+
"previousCursor"
|
|
325
|
+
]);
|
|
326
|
+
const clientStateKeys = new Set([
|
|
327
|
+
"rootLists",
|
|
328
|
+
"rootRequests",
|
|
329
|
+
"store"
|
|
330
|
+
]);
|
|
331
|
+
const storeStateKeys = new Set([
|
|
332
|
+
"coverage",
|
|
333
|
+
"lists",
|
|
334
|
+
"records"
|
|
335
|
+
]);
|
|
336
|
+
const isEntry = (value) => Array.isArray(value) && value.length === 2 && typeof value[0] === "string";
|
|
337
|
+
const isOptionalLimit = (value) => value === void 0 || typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
338
|
+
const isPagination = (value) => value === void 0 || isRecord$1(value) && hasOnlyKeys(value, paginationKeys) && typeof value.hasNext === "boolean" && typeof value.hasPrevious === "boolean" && (value.nextCursor === void 0 || typeof value.nextCursor === "string") && (value.previousCursor === void 0 || typeof value.previousCursor === "string");
|
|
339
|
+
const isList = (value) => {
|
|
340
|
+
if (!isRecord$1(value) || !hasOnlyKeys(value, listKeys) || !isStringArray(value.ids) || hasDuplicates(value.ids) || value.cursors !== void 0 && (!Array.isArray(value.cursors) || value.cursors.length !== value.ids.length)) return false;
|
|
341
|
+
return (value.cursors === void 0 || Array.isArray(value.cursors) && value.cursors.every((entry) => entry === void 0 || typeof entry === "string")) && (value.liveAfterIds === void 0 || isUniqueStringArray(value.liveAfterIds)) && (value.liveBeforeIds === void 0 || isUniqueStringArray(value.liveBeforeIds)) && isOptionalLimit(value.backwardPageLimit) && isOptionalLimit(value.forwardPageLimit) && isPagination(value.pagination) && (value.pendingAfterIds === void 0 || isUniqueStringArray(value.pendingAfterIds)) && (value.pendingBeforeIds === void 0 || isUniqueStringArray(value.pendingBeforeIds));
|
|
342
|
+
};
|
|
343
|
+
const isFieldPath = (value) => value.length > 0 && value.split(".").every((segment) => segment.length > 0);
|
|
344
|
+
const isFieldPathArray = (value) => isUniqueStringArray(value) && value.every(isFieldPath);
|
|
345
|
+
const hasDuplicateEntries = (entries) => hasDuplicates(entries.map((entry) => entry[0]));
|
|
346
|
+
const decodeClientHydrationState = (value, limits) => {
|
|
347
|
+
const decoded = decodeHydrationValue(value, limits);
|
|
348
|
+
if (!isRecord$1(decoded) || !hasOnlyKeys(decoded, clientStateKeys) || !Array.isArray(decoded.rootLists) || !Array.isArray(decoded.rootRequests) || !isRecord$1(decoded.store) || !hasOnlyKeys(decoded.store, storeStateKeys) || !Array.isArray(decoded.store.coverage) || !Array.isArray(decoded.store.lists) || !Array.isArray(decoded.store.records)) throw invalidPayload("data");
|
|
349
|
+
if (decoded.rootLists.some((entry) => !isEntry(entry) || !isStringArray(entry[1])) || decoded.rootRequests.some((entry) => !isEntry(entry) || entry[1] !== null && typeof entry[1] !== "string") || decoded.store.coverage.some((entry) => !isEntry(entry) || !isFieldPathArray(entry[1])) || decoded.store.lists.some((entry) => !isEntry(entry) || !isList(entry[1])) || decoded.store.records.some((entry) => !isEntry(entry) || !isRecord$1(entry[1]))) throw invalidPayload("data");
|
|
350
|
+
if (hasDuplicateEntries(decoded.rootLists) || hasDuplicateEntries(decoded.rootRequests) || hasDuplicateEntries(decoded.store.coverage) || hasDuplicateEntries(decoded.store.lists) || hasDuplicateEntries(decoded.store.records) || decoded.rootLists.some(([, keys]) => hasDuplicates(keys))) throw invalidPayload("data");
|
|
351
|
+
return decoded;
|
|
352
|
+
};
|
|
353
|
+
//#endregion
|
|
66
354
|
//#region src/mask.ts
|
|
67
355
|
function emptyMask() {
|
|
68
356
|
return {
|
|
@@ -111,6 +399,18 @@ function fromPaths(paths) {
|
|
|
111
399
|
for (const path of paths) addPath(mask, path);
|
|
112
400
|
return mask;
|
|
113
401
|
}
|
|
402
|
+
function toPaths(mask) {
|
|
403
|
+
const paths = [];
|
|
404
|
+
const visit = (current, prefix) => {
|
|
405
|
+
if (current.all) {
|
|
406
|
+
if (prefix) paths.push(prefix);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
for (const [key, child] of current.children) visit(child, prefix ? `${prefix}.${key}` : key);
|
|
410
|
+
};
|
|
411
|
+
visit(mask, null);
|
|
412
|
+
return paths;
|
|
413
|
+
}
|
|
114
414
|
function isCovered(mask, path) {
|
|
115
415
|
if (mask.all) return true;
|
|
116
416
|
const parts = path.split(".");
|
|
@@ -138,7 +438,7 @@ function intersects(a, b) {
|
|
|
138
438
|
return false;
|
|
139
439
|
}
|
|
140
440
|
//#endregion
|
|
141
|
-
//#region ../../node_modules/.pnpm/@trpc+server@11.17.0_typescript@
|
|
441
|
+
//#region ../../node_modules/.pnpm/@trpc+server@11.17.0_typescript@6.0.3/node_modules/@trpc/server/dist/getErrorShape-BPSzUA7W.mjs
|
|
142
442
|
var __create = Object.create;
|
|
143
443
|
var __defProp = Object.defineProperty;
|
|
144
444
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -264,41 +564,6 @@ __toESM(__commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_
|
|
|
264
564
|
module.exports = _objectSpread2, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
265
565
|
} })(), 1);
|
|
266
566
|
//#endregion
|
|
267
|
-
//#region src/types.ts
|
|
268
|
-
/** Internal marker added to objects that represent a view payload. */
|
|
269
|
-
const ViewKind = Symbol("__fate__view");
|
|
270
|
-
/** Symbol used to attach the set of view tags that were spread into a ref or masked record. */
|
|
271
|
-
const ViewsTag = Symbol("__fate__views");
|
|
272
|
-
/** Symbol used to brand a value as a node reference inside the cache. */
|
|
273
|
-
const NodeRefTag = Symbol("__fate__node-ref");
|
|
274
|
-
/** Symbol attached to connection results so pagination helpers can find their metadata. */
|
|
275
|
-
const ConnectionTag = Symbol("__fate__connection");
|
|
276
|
-
const viewTag = "__fate-view__";
|
|
277
|
-
/** Generates a stable view tag for a view definition. */
|
|
278
|
-
function getViewTag(id) {
|
|
279
|
-
return `${viewTag}${id}`;
|
|
280
|
-
}
|
|
281
|
-
/** Determines whether a property key is a fate view tag. */
|
|
282
|
-
function isViewTag(key) {
|
|
283
|
-
return key.startsWith(viewTag);
|
|
284
|
-
}
|
|
285
|
-
/** Indicates whether a request item represents an explicit node ID. */
|
|
286
|
-
function isNodeItem(item) {
|
|
287
|
-
return "id" in item;
|
|
288
|
-
}
|
|
289
|
-
/** Indicates whether a request item represents explicit node IDs. */
|
|
290
|
-
function isNodesItem(item) {
|
|
291
|
-
return "ids" in item;
|
|
292
|
-
}
|
|
293
|
-
/** Indicates whether a request item represents a root query. */
|
|
294
|
-
function isQueryItem(item) {
|
|
295
|
-
return "view" in item && !("id" in item) && !("ids" in item);
|
|
296
|
-
}
|
|
297
|
-
/** Brand used on root definitions to mark their identity in the d.ts output. */
|
|
298
|
-
const RootKind = "__fate__root";
|
|
299
|
-
/** Brand used on mutation definitions to mark their identity in the d.ts output. */
|
|
300
|
-
const MutationKind = "__fate__mutation";
|
|
301
|
-
//#endregion
|
|
302
567
|
//#region src/view.ts
|
|
303
568
|
/**
|
|
304
569
|
* Collects all view payloads that apply to the given ref.
|
|
@@ -455,6 +720,7 @@ const getSelectionPlan = (viewComposition, ref) => {
|
|
|
455
720
|
if (value) paths.add(path);
|
|
456
721
|
continue;
|
|
457
722
|
}
|
|
723
|
+
if (isDeferredSelection(value)) continue;
|
|
458
724
|
if (isViewTag(key)) {
|
|
459
725
|
if (!ref || ref[ViewsTag] && ref[ViewsTag].has(key)) walk(value.select, prefix);
|
|
460
726
|
continue;
|
|
@@ -492,6 +758,9 @@ const getSelectionPlan = (viewComposition, ref) => {
|
|
|
492
758
|
paths
|
|
493
759
|
};
|
|
494
760
|
};
|
|
761
|
+
const getDeferredSelectionPlan = (field, selection) => {
|
|
762
|
+
return getSelectionPlan({ [field]: selection }, null);
|
|
763
|
+
};
|
|
495
764
|
//#endregion
|
|
496
765
|
//#region src/mutation.ts
|
|
497
766
|
/**
|
|
@@ -593,7 +862,7 @@ function wrapMutation(client, identifier) {
|
|
|
593
862
|
client.clearOptimisticUpdate(optimisticToken);
|
|
594
863
|
}
|
|
595
864
|
};
|
|
596
|
-
const mutationPromise = performMutation
|
|
865
|
+
const mutationPromise = client.trackPendingRequest(performMutation);
|
|
597
866
|
if (optimisticEntityId) client.registerPendingOptimisticMutation(optimisticEntityId, mutationPromise);
|
|
598
867
|
return mutationPromise;
|
|
599
868
|
};
|
|
@@ -621,24 +890,6 @@ const categorizeHTTPErrorStatus = (statusCode) => {
|
|
|
621
890
|
}
|
|
622
891
|
};
|
|
623
892
|
//#endregion
|
|
624
|
-
//#region src/node-ref.ts
|
|
625
|
-
function createNodeRef(id) {
|
|
626
|
-
const ref = {};
|
|
627
|
-
Object.defineProperty(ref, NodeRefTag, {
|
|
628
|
-
configurable: false,
|
|
629
|
-
enumerable: false,
|
|
630
|
-
value: id,
|
|
631
|
-
writable: false
|
|
632
|
-
});
|
|
633
|
-
return Object.freeze(ref);
|
|
634
|
-
}
|
|
635
|
-
function isNodeRef(value) {
|
|
636
|
-
return !value || typeof value !== "object" ? false : NodeRefTag in value;
|
|
637
|
-
}
|
|
638
|
-
function getNodeRefId(ref) {
|
|
639
|
-
return ref[NodeRefTag];
|
|
640
|
-
}
|
|
641
|
-
//#endregion
|
|
642
893
|
//#region src/operation-lifetime.ts
|
|
643
894
|
var OperationLifetime = class {
|
|
644
895
|
constructor(releaseBufferSize) {
|
|
@@ -726,6 +977,45 @@ const cloneValue = (value) => {
|
|
|
726
977
|
}
|
|
727
978
|
return value;
|
|
728
979
|
};
|
|
980
|
+
const isPlainRecord = (value) => {
|
|
981
|
+
if (value == null || typeof value !== "object" || Array.isArray(value) || isNodeRef(value)) return false;
|
|
982
|
+
const prototype = Object.getPrototypeOf(value);
|
|
983
|
+
return prototype === Object.prototype || prototype === null;
|
|
984
|
+
};
|
|
985
|
+
const isDate = (value) => value != null && typeof value === "object" && Object.prototype.toString.call(value) === "[object Date]" && typeof value.getTime === "function";
|
|
986
|
+
const mergePreservingExisting = (incoming, existing) => {
|
|
987
|
+
if (!isPlainRecord(incoming) || !isPlainRecord(existing)) return existing;
|
|
988
|
+
const result = Object.create(null);
|
|
989
|
+
for (const [key, value] of Object.entries(incoming)) Object.defineProperty(result, key, {
|
|
990
|
+
configurable: true,
|
|
991
|
+
enumerable: true,
|
|
992
|
+
value,
|
|
993
|
+
writable: true
|
|
994
|
+
});
|
|
995
|
+
for (const [key, value] of Object.entries(existing)) Object.defineProperty(result, key, {
|
|
996
|
+
configurable: true,
|
|
997
|
+
enumerable: true,
|
|
998
|
+
value: Object.hasOwn(result, key) ? mergePreservingExisting(result[key], value) : value,
|
|
999
|
+
writable: true
|
|
1000
|
+
});
|
|
1001
|
+
return result;
|
|
1002
|
+
};
|
|
1003
|
+
const areHydrationValuesEqual = (left, right) => {
|
|
1004
|
+
if (Object.is(left, right)) return true;
|
|
1005
|
+
if (isNodeRef(left) || isNodeRef(right)) return isNodeRef(left) && isNodeRef(right) && getNodeRefId(left) === getNodeRefId(right);
|
|
1006
|
+
if (isDate(left) || isDate(right)) return isDate(left) && isDate(right) && left.getTime() === right.getTime();
|
|
1007
|
+
if (Array.isArray(left) || Array.isArray(right)) return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => areHydrationValuesEqual(value, right[index]));
|
|
1008
|
+
if (!isPlainRecord(left) || !isPlainRecord(right)) return false;
|
|
1009
|
+
const leftKeys = Object.keys(left);
|
|
1010
|
+
const rightKeys = Object.keys(right);
|
|
1011
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(right, key) && areHydrationValuesEqual(left[key], right[key]));
|
|
1012
|
+
};
|
|
1013
|
+
const areMasksEqual = (left, right) => {
|
|
1014
|
+
if (!left || !right) return left === right;
|
|
1015
|
+
const leftPaths = toPaths(left).sort();
|
|
1016
|
+
const rightPaths = toPaths(right).sort();
|
|
1017
|
+
return leftPaths.length === rightPaths.length && leftPaths.every((path, index) => path === rightPaths[index]);
|
|
1018
|
+
};
|
|
729
1019
|
const emptyFunction = () => {};
|
|
730
1020
|
var Store = class {
|
|
731
1021
|
constructor() {
|
|
@@ -739,6 +1029,58 @@ var Store = class {
|
|
|
739
1029
|
this.subscriptions = /* @__PURE__ */ new Map();
|
|
740
1030
|
this.listSubscriptions = /* @__PURE__ */ new Map();
|
|
741
1031
|
}
|
|
1032
|
+
dehydrate() {
|
|
1033
|
+
return {
|
|
1034
|
+
coverage: [...this.coverage].map(([id, mask]) => [id, toPaths(mask)]),
|
|
1035
|
+
lists: [...this.lists],
|
|
1036
|
+
records: [...this.records]
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
hydrate(state, mode, options = {}) {
|
|
1040
|
+
const previousCoverage = this.coverage;
|
|
1041
|
+
const previousLists = this.lists;
|
|
1042
|
+
const previousRecords = this.records;
|
|
1043
|
+
const coverage = mode === "replace" ? /* @__PURE__ */ new Map() : new Map([...previousCoverage].map(([id, mask]) => [id, cloneMask(mask)]));
|
|
1044
|
+
const lists = mode === "replace" ? /* @__PURE__ */ new Map() : new Map(previousLists);
|
|
1045
|
+
const records = mode === "replace" ? /* @__PURE__ */ new Map() : new Map(previousRecords);
|
|
1046
|
+
for (const [id, incoming] of state.records) {
|
|
1047
|
+
const previous = records.get(id);
|
|
1048
|
+
const next = previous && mode === "preserve-existing" ? mergePreservingExisting(incoming, previous) : incoming;
|
|
1049
|
+
records.set(id, previous && areHydrationValuesEqual(previous, next) ? previous : next);
|
|
1050
|
+
}
|
|
1051
|
+
for (const [id, paths] of state.coverage) {
|
|
1052
|
+
const incoming = fromPaths(paths);
|
|
1053
|
+
const previous = coverage.get(id);
|
|
1054
|
+
if (previous && mode === "preserve-existing") union(previous, incoming);
|
|
1055
|
+
else coverage.set(id, incoming);
|
|
1056
|
+
}
|
|
1057
|
+
for (const [key, list] of state.lists) {
|
|
1058
|
+
const previous = lists.get(key);
|
|
1059
|
+
if (mode === "replace" || !previous) lists.set(key, previous && areHydrationValuesEqual(previous, list) ? previous : list);
|
|
1060
|
+
}
|
|
1061
|
+
this.coverage = coverage;
|
|
1062
|
+
this.lists = lists;
|
|
1063
|
+
this.records = records;
|
|
1064
|
+
this.rebuildIndexes();
|
|
1065
|
+
const changedRecordIds = /* @__PURE__ */ new Set();
|
|
1066
|
+
for (const id of new Set([...previousRecords.keys(), ...records.keys()])) if (!areHydrationValuesEqual(previousRecords.get(id), records.get(id)) || !areMasksEqual(previousCoverage.get(id), coverage.get(id))) changedRecordIds.add(id);
|
|
1067
|
+
const changedListKeys = /* @__PURE__ */ new Set();
|
|
1068
|
+
for (const key of new Set([...previousLists.keys(), ...lists.keys()])) if (!areHydrationValuesEqual(previousLists.get(key), lists.get(key))) changedListKeys.add(key);
|
|
1069
|
+
const notify = () => {
|
|
1070
|
+
for (const id of changedRecordIds) this.notify(id);
|
|
1071
|
+
for (const key of changedListKeys) this.notifyListSubscribers(key);
|
|
1072
|
+
};
|
|
1073
|
+
if (!options.deferNotifications) notify();
|
|
1074
|
+
return notify;
|
|
1075
|
+
}
|
|
1076
|
+
rebuildIndexes() {
|
|
1077
|
+
this.listKeysByOwnerField.clear();
|
|
1078
|
+
this.listKeysByReferencedEntity.clear();
|
|
1079
|
+
this.recordReferenceFields.clear();
|
|
1080
|
+
this.recordReferencesByTarget.clear();
|
|
1081
|
+
for (const [id, record] of this.records) this.addRecordReferenceIndexes(id, record);
|
|
1082
|
+
for (const [key, list] of this.lists) this.addListIndexes(key, list);
|
|
1083
|
+
}
|
|
742
1084
|
read(id) {
|
|
743
1085
|
return this.records.get(id);
|
|
744
1086
|
}
|
|
@@ -1337,6 +1679,14 @@ const getId = (record) => {
|
|
|
1337
1679
|
};
|
|
1338
1680
|
const emptySet = /* @__PURE__ */ new Set();
|
|
1339
1681
|
const emptyDispose = () => void 0;
|
|
1682
|
+
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
1683
|
+
const getDefaultHydrationScope = (roots, types) => JSON.stringify({
|
|
1684
|
+
roots: Object.entries(roots ?? {}).map(([name, root]) => [name, root.type]).sort(([left], [right]) => compareStrings(left, right)),
|
|
1685
|
+
types: types.map(({ fields, type }) => ({
|
|
1686
|
+
fields: Object.entries(fields ?? {}).map(([field, descriptor]) => [field, descriptor]).sort(([left], [right]) => compareStrings(left, right)),
|
|
1687
|
+
type
|
|
1688
|
+
})).sort((left, right) => compareStrings(left.type, right.type))
|
|
1689
|
+
});
|
|
1340
1690
|
const dropCanonicalListIds = (listIds, ids) => {
|
|
1341
1691
|
if (!listIds || listIds.length === 0) return;
|
|
1342
1692
|
const canonicalIds = new Set(ids);
|
|
@@ -1464,6 +1814,7 @@ var FateClient = class {
|
|
|
1464
1814
|
this.parentLists = /* @__PURE__ */ new Map();
|
|
1465
1815
|
this.rootLists = /* @__PURE__ */ new Map();
|
|
1466
1816
|
this.pending = /* @__PURE__ */ new Map();
|
|
1817
|
+
this.pendingDeferred = /* @__PURE__ */ new Map();
|
|
1467
1818
|
this.pendingOptimisticMutations = /* @__PURE__ */ new Map();
|
|
1468
1819
|
this.optimisticMasks = /* @__PURE__ */ new Map();
|
|
1469
1820
|
this.optimisticByEntity = /* @__PURE__ */ new Map();
|
|
@@ -1477,6 +1828,7 @@ var FateClient = class {
|
|
|
1477
1828
|
this.stalledRequests = /* @__PURE__ */ new Set();
|
|
1478
1829
|
this.gcPending = false;
|
|
1479
1830
|
this.gcScheduled = false;
|
|
1831
|
+
this.pendingNetworkRequests = 0;
|
|
1480
1832
|
this.store = new Store();
|
|
1481
1833
|
this.viewDataCache = new ViewDataCache();
|
|
1482
1834
|
this.liveConnectionInvalidationToken = 0;
|
|
@@ -1485,6 +1837,10 @@ var FateClient = class {
|
|
|
1485
1837
|
this.mutations = Object.create(null);
|
|
1486
1838
|
this.onLiveError = options.onLiveError;
|
|
1487
1839
|
this.operationLifetime = new OperationLifetime(options.gcReleaseBufferSize ?? 10);
|
|
1840
|
+
this.hydrationLimits = resolveHydrationLimits(options.hydrationLimits);
|
|
1841
|
+
const hydrationScope = options.hydrationScope ?? getDefaultHydrationScope(options.roots, options.types);
|
|
1842
|
+
if (typeof hydrationScope !== "string" || hydrationScope.length === 0) throw new Error(`fate: Hydration scope must be a non-empty string.`);
|
|
1843
|
+
this.hydrationScope = hydrationScope;
|
|
1488
1844
|
this.roots = options.roots;
|
|
1489
1845
|
this.transport = options.transport;
|
|
1490
1846
|
this.types = new Map(options.types.map((entity) => [entity.type, {
|
|
@@ -1529,6 +1885,56 @@ var FateClient = class {
|
|
|
1529
1885
|
}
|
|
1530
1886
|
}
|
|
1531
1887
|
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Exports the durable normalized cache as an opaque JSON-safe value for SSR
|
|
1890
|
+
* transfer or persistence. Runtime requests, subscriptions, retainers, and
|
|
1891
|
+
* optimistic state are intentionally excluded.
|
|
1892
|
+
*/
|
|
1893
|
+
dehydrate() {
|
|
1894
|
+
if (this.hasActiveOptimisticUpdates()) throw new Error(`fate: Cannot dehydrate while optimistic updates are active.`);
|
|
1895
|
+
if (this.hasPendingRequests()) throw new Error(`fate: Cannot dehydrate while requests are pending.`);
|
|
1896
|
+
return {
|
|
1897
|
+
data: encodeHydrationValue({
|
|
1898
|
+
rootLists: [...this.rootLists].map(([type, keys]) => [type, [...keys]]),
|
|
1899
|
+
rootRequests: [...this.rootRequests],
|
|
1900
|
+
store: this.store.dehydrate()
|
|
1901
|
+
}, this.hydrationLimits),
|
|
1902
|
+
scope: this.hydrationScope,
|
|
1903
|
+
version: 1
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Restores a durable normalized cache snapshot without restoring runtime
|
|
1908
|
+
* request handles, retainers, subscriptions, or optimistic update state.
|
|
1909
|
+
* Hydrate before rendering SSR content. Replaying the same snapshot is safe.
|
|
1910
|
+
*/
|
|
1911
|
+
hydrate(state, options = {}) {
|
|
1912
|
+
if (!state || typeof state !== "object" || state.version !== 1) throw new Error(`fate: Unsupported hydration state version.`);
|
|
1913
|
+
if (state.scope !== this.hydrationScope) throw new Error(`fate: Hydration state scope does not match this client.`);
|
|
1914
|
+
if (this.hasActiveOptimisticUpdates()) throw new Error(`fate: Cannot hydrate while optimistic updates are active.`);
|
|
1915
|
+
if (this.hasPendingRequests()) throw new Error(`fate: Cannot hydrate while requests are pending.`);
|
|
1916
|
+
const mode = options.merge ?? "preserve-existing";
|
|
1917
|
+
if (mode !== "preserve-existing" && mode !== "replace") throw new Error(`fate: Unsupported hydration merge mode.`);
|
|
1918
|
+
const decoded = decodeClientHydrationState(state.data, this.hydrationLimits);
|
|
1919
|
+
const notify = this.store.hydrate(decoded.store, mode, { deferNotifications: true });
|
|
1920
|
+
if (mode === "replace") {
|
|
1921
|
+
this.rootLists.clear();
|
|
1922
|
+
this.rootRequests.clear();
|
|
1923
|
+
this.stableRefs.clear();
|
|
1924
|
+
}
|
|
1925
|
+
for (const [type, incomingKeys] of decoded.rootLists) {
|
|
1926
|
+
let keys = this.rootLists.get(type);
|
|
1927
|
+
if (!keys) {
|
|
1928
|
+
keys = /* @__PURE__ */ new Set();
|
|
1929
|
+
this.rootLists.set(type, keys);
|
|
1930
|
+
}
|
|
1931
|
+
for (const key of incomingKeys) keys.add(key);
|
|
1932
|
+
}
|
|
1933
|
+
for (const [key, entityId] of decoded.rootRequests) if (mode === "replace" || !this.rootRequests.has(key)) this.rootRequests.set(key, entityId);
|
|
1934
|
+
this.stalledRequests.clear();
|
|
1935
|
+
this.viewDataCache.clear();
|
|
1936
|
+
notify();
|
|
1937
|
+
}
|
|
1532
1938
|
registerRootList(type, key) {
|
|
1533
1939
|
let lists = this.rootLists.get(type);
|
|
1534
1940
|
if (!lists) {
|
|
@@ -1597,7 +2003,7 @@ var FateClient = class {
|
|
|
1597
2003
|
...baseRecord,
|
|
1598
2004
|
args: argsPayload
|
|
1599
2005
|
} : argsPayload ? { args: argsPayload } : input;
|
|
1600
|
-
return await this.transport.mutate(key, requestInput, select);
|
|
2006
|
+
return await this.trackPendingRequest(() => this.transport.mutate(key, requestInput, select));
|
|
1601
2007
|
}
|
|
1602
2008
|
write(type, data, select, snapshots, plan, pathPrefix = null, blockedMask, insert, listSnapshots) {
|
|
1603
2009
|
return this.writeEntity(type, data, select, snapshots, plan, pathPrefix, blockedMask, insert, listSnapshots);
|
|
@@ -1690,19 +2096,81 @@ var FateClient = class {
|
|
|
1690
2096
|
if (this.stalledRequests.has(key)) return resolveSnapshot();
|
|
1691
2097
|
const pendingPromise = this.pending.get(key) || null;
|
|
1692
2098
|
if (pendingPromise) return pendingPromise;
|
|
1693
|
-
const promise = this.
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
2099
|
+
const promise = this.trackPendingRequest(async () => {
|
|
2100
|
+
await this.fetchByIdAndNormalize(type, [id], missing, plan);
|
|
2101
|
+
try {
|
|
2102
|
+
if (this.store.missingForSelection(entityId, selectedPaths).size > 0) {
|
|
2103
|
+
this.stalledRequests.add(key);
|
|
2104
|
+
return resolveSnapshot();
|
|
2105
|
+
}
|
|
2106
|
+
this.stalledRequests.delete(key);
|
|
2107
|
+
return this.readView(view, ref);
|
|
2108
|
+
} finally {
|
|
2109
|
+
this.pending.delete(key);
|
|
1697
2110
|
}
|
|
1698
|
-
this.stalledRequests.delete(key);
|
|
1699
|
-
return this.readView(view, ref);
|
|
1700
2111
|
});
|
|
1701
2112
|
this.pending.set(key, promise);
|
|
1702
2113
|
return promise;
|
|
1703
2114
|
}
|
|
1704
2115
|
return resolveSnapshot();
|
|
1705
2116
|
}
|
|
2117
|
+
readDeferred(deferred) {
|
|
2118
|
+
const metadata = getDeferredMetadata(deferred);
|
|
2119
|
+
const resolvedOwner = this.optimisticEntityResolutions.get(metadata.owner) ?? metadata.owner;
|
|
2120
|
+
const parsedOwner = parseEntityId(resolvedOwner);
|
|
2121
|
+
const ownerRecord = this.store.read(resolvedOwner);
|
|
2122
|
+
const rawOwnerId = ownerRecord && (typeof ownerRecord.id === "string" || typeof ownerRecord.id === "number") ? ownerRecord.id : parsedOwner.id;
|
|
2123
|
+
const ownerType = parsedOwner.type || metadata.type;
|
|
2124
|
+
const plan = getDeferredSelectionPlan(metadata.field, metadata.selection);
|
|
2125
|
+
const selectedPaths = plan.paths;
|
|
2126
|
+
const missing = this.store.missingForSelection(resolvedOwner, selectedPaths);
|
|
2127
|
+
const hasMissingList = () => this.hasMissingDeferredList(resolvedOwner, ownerType, metadata.field, plan);
|
|
2128
|
+
const listMissing = hasMissingList();
|
|
2129
|
+
const fetchPaths = listMissing ? selectedPaths : missing;
|
|
2130
|
+
const resolveSnapshot = () => {
|
|
2131
|
+
const snapshot = this.readViewSelection({ [metadata.field]: metadata.selection }, this.stableRefWithViewNames(ownerType, rawOwnerId, /* @__PURE__ */ new Set()), resolvedOwner, plan);
|
|
2132
|
+
const resolved = {
|
|
2133
|
+
coverage: snapshot.coverage,
|
|
2134
|
+
data: snapshot.data[metadata.field]
|
|
2135
|
+
};
|
|
2136
|
+
return {
|
|
2137
|
+
status: "fulfilled",
|
|
2138
|
+
then: (onfulfilled, onrejected) => Promise.resolve(resolved).then(onfulfilled, onrejected),
|
|
2139
|
+
value: resolved
|
|
2140
|
+
};
|
|
2141
|
+
};
|
|
2142
|
+
if (missing.size === 0 && !listMissing) {
|
|
2143
|
+
this.clearStalledRequestsForEntity(resolvedOwner);
|
|
2144
|
+
return resolveSnapshot();
|
|
2145
|
+
}
|
|
2146
|
+
const key = this.deferredPendingKey(resolvedOwner, fetchPaths, plan, this.deferredSelectionSignature(metadata.selection));
|
|
2147
|
+
if (this.stalledRequests.has(key)) return resolveSnapshot();
|
|
2148
|
+
const pendingOptimistic = this.getPendingOptimisticMutations(resolvedOwner);
|
|
2149
|
+
if (pendingOptimistic) {
|
|
2150
|
+
const pending = this.pendingDeferred.get(key);
|
|
2151
|
+
if (pending) return pending;
|
|
2152
|
+
const promise = Promise.all(pendingOptimistic).then(() => this.readDeferred(deferred)).finally(() => this.pendingDeferred.delete(key));
|
|
2153
|
+
this.pendingDeferred.set(key, promise);
|
|
2154
|
+
return promise;
|
|
2155
|
+
}
|
|
2156
|
+
const pendingPromise = this.pendingDeferred.get(key) || null;
|
|
2157
|
+
if (pendingPromise) return pendingPromise;
|
|
2158
|
+
const promise = this.trackPendingRequest(async () => {
|
|
2159
|
+
try {
|
|
2160
|
+
await this.fetchByIdAndNormalize(ownerType, [rawOwnerId], fetchPaths, plan);
|
|
2161
|
+
if (this.store.missingForSelection(resolvedOwner, selectedPaths).size > 0 || hasMissingList()) {
|
|
2162
|
+
this.stalledRequests.add(key);
|
|
2163
|
+
return resolveSnapshot().value;
|
|
2164
|
+
}
|
|
2165
|
+
this.stalledRequests.delete(key);
|
|
2166
|
+
return this.readDeferred(deferred).value;
|
|
2167
|
+
} finally {
|
|
2168
|
+
this.pendingDeferred.delete(key);
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
this.pendingDeferred.set(key, promise);
|
|
2172
|
+
return promise;
|
|
2173
|
+
}
|
|
1706
2174
|
assertLiveViewSupport() {
|
|
1707
2175
|
if (!this.transport.subscribeById) throw new Error(`fate: transport does not support live views. Please provide a 'subscribeById' implementation in your transport.`);
|
|
1708
2176
|
}
|
|
@@ -2134,6 +2602,9 @@ var FateClient = class {
|
|
|
2134
2602
|
return filtered;
|
|
2135
2603
|
}
|
|
2136
2604
|
async loadConnection(view, connection, args, options = {}) {
|
|
2605
|
+
return await this.trackPendingRequest(() => this.loadConnectionInternal(view, connection, args, options));
|
|
2606
|
+
}
|
|
2607
|
+
async loadConnectionInternal(view, connection, args, options = {}) {
|
|
2137
2608
|
const direction = options.direction ?? "forward";
|
|
2138
2609
|
if (connection.root) {
|
|
2139
2610
|
if (!this.transport.fetchList) throw new Error(`fate: transport does not support list fetching. Please add support for 'fetchList' in your transport.`);
|
|
@@ -2379,6 +2850,17 @@ var FateClient = class {
|
|
|
2379
2850
|
hasActiveOptimisticUpdates() {
|
|
2380
2851
|
return this.optimisticMasks.size > 0 || this.optimisticByEntity.size > 0 || this.pendingOptimisticMutations.size > 0;
|
|
2381
2852
|
}
|
|
2853
|
+
hasPendingRequests() {
|
|
2854
|
+
return this.pendingNetworkRequests > 0;
|
|
2855
|
+
}
|
|
2856
|
+
async trackPendingRequest(request) {
|
|
2857
|
+
this.pendingNetworkRequests += 1;
|
|
2858
|
+
try {
|
|
2859
|
+
return await request();
|
|
2860
|
+
} finally {
|
|
2861
|
+
this.pendingNetworkRequests -= 1;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2382
2864
|
runPendingGarbageCollection() {
|
|
2383
2865
|
if (this.gcPending && !this.hasActiveOptimisticUpdates()) this.gc();
|
|
2384
2866
|
}
|
|
@@ -2531,35 +3013,41 @@ var FateClient = class {
|
|
|
2531
3013
|
}
|
|
2532
3014
|
async fetchByIdAndNormalize(type, ids, select, plan, prefix = null) {
|
|
2533
3015
|
const resolvedArgs = resolvedArgsFromPlan(plan);
|
|
2534
|
-
|
|
2535
|
-
|
|
3016
|
+
await this.trackPendingRequest(async () => {
|
|
3017
|
+
const records = await this.transport.fetchById(type, ids, select, resolvedArgs);
|
|
3018
|
+
for (const record of records) this.writeEntity(type, record, select, void 0, plan, prefix);
|
|
3019
|
+
});
|
|
2536
3020
|
}
|
|
2537
3021
|
async fetchQueryAndNormalize(item) {
|
|
2538
3022
|
if (!this.transport.fetchQuery) throw new Error(`fate: transport does not support queries. Please add support for 'fetchQuery' in your transport for '${item.name}'.`);
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
3023
|
+
await this.trackPendingRequest(async () => {
|
|
3024
|
+
const record = await this.transport.fetchQuery(item.name, item.plan.paths, item.argsPayload);
|
|
3025
|
+
if (!record || typeof record !== "object") {
|
|
3026
|
+
this.rootRequests.set(item.queryKey, null);
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
const entityId = this.writeEntity(item.type, record, item.plan.paths, void 0, item.plan);
|
|
3030
|
+
this.rootRequests.set(item.queryKey, entityId);
|
|
3031
|
+
});
|
|
2546
3032
|
}
|
|
2547
3033
|
async fetchListAndNormalize(item) {
|
|
2548
3034
|
if (!this.transport.fetchList) throw new Error(`fate: 'transport.fetchList' is not configured but request includes a list for key '${item.name}'.`);
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
3035
|
+
await this.trackPendingRequest(async () => {
|
|
3036
|
+
const { items, pagination } = await this.transport.fetchList(item.name, item.plan.paths, item.argsPayload);
|
|
3037
|
+
const ids = [];
|
|
3038
|
+
const cursors = [];
|
|
3039
|
+
for (const entry of items) {
|
|
3040
|
+
const id = this.writeEntity(item.type, entry.node, item.plan.paths, void 0, item.plan);
|
|
3041
|
+
ids.push(id);
|
|
3042
|
+
cursors.push(entry.cursor);
|
|
3043
|
+
}
|
|
3044
|
+
if (!filterConnectionArgs(item.argsPayload)) this.registerRootList(item.type, item.listKey);
|
|
3045
|
+
const previous = this.store.getListState(item.listKey);
|
|
3046
|
+
this.store.setList(item.listKey, this.mergeListState(previous, ids, cursors, pagination, {
|
|
3047
|
+
...getPaginationMergeInfo(item.argsPayload),
|
|
3048
|
+
replace: true
|
|
3049
|
+
}));
|
|
3050
|
+
});
|
|
2563
3051
|
}
|
|
2564
3052
|
writeEntity(type, record, select, snapshots, plan, pathPrefix = null, blockedMask, insert, listSnapshots) {
|
|
2565
3053
|
const config = this.types.get(type);
|
|
@@ -2726,8 +3214,20 @@ var FateClient = class {
|
|
|
2726
3214
|
target[ViewsTag].add(key);
|
|
2727
3215
|
continue;
|
|
2728
3216
|
}
|
|
2729
|
-
coverageById.set(parentId, (coverageById.get(parentId) ?? /* @__PURE__ */ new Set()).add(key));
|
|
2730
3217
|
const fieldPath = prefix ? `${prefix}.${key}` : key;
|
|
3218
|
+
if (isDeferredSelection(selectionKind)) {
|
|
3219
|
+
const { id, type } = parseEntityId(parentId);
|
|
3220
|
+
coverageById.set(parentId, (coverageById.get(parentId) ?? /* @__PURE__ */ new Set()).add(key));
|
|
3221
|
+
target[key] = createDeferred({
|
|
3222
|
+
field: key,
|
|
3223
|
+
id,
|
|
3224
|
+
owner: parentId,
|
|
3225
|
+
selection: getDeferredSelection(selectionKind),
|
|
3226
|
+
type
|
|
3227
|
+
});
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
3230
|
+
coverageById.set(parentId, (coverageById.get(parentId) ?? /* @__PURE__ */ new Set()).add(key));
|
|
2731
3231
|
const selectionType = typeof selectionKind;
|
|
2732
3232
|
if (selectionType === "boolean" && selectionKind) target[key] = record[key];
|
|
2733
3233
|
else if (selectionKind && selectionType === "object") {
|
|
@@ -2846,7 +3346,9 @@ var FateClient = class {
|
|
|
2846
3346
|
}
|
|
2847
3347
|
};
|
|
2848
3348
|
const data = { __typename: parseEntityId(entityId).type };
|
|
2849
|
-
|
|
3349
|
+
const payloads = getViewPayloads(viewComposition, ref);
|
|
3350
|
+
if (payloads.length === 0 && isRecord(viewComposition)) walk(viewComposition, record, data, entityId, pathPrefix);
|
|
3351
|
+
else for (const viewPayload of payloads) walk(viewPayload.select, record, data, entityId, pathPrefix);
|
|
2850
3352
|
return {
|
|
2851
3353
|
coverage: [...coverageById.entries()],
|
|
2852
3354
|
data
|
|
@@ -2862,6 +3364,32 @@ var FateClient = class {
|
|
|
2862
3364
|
pendingKey(entityId, missingFields) {
|
|
2863
3365
|
return `${this.pendingPrefix(entityId)}${[...missingFields].sort().join("|")}`;
|
|
2864
3366
|
}
|
|
3367
|
+
deferredPendingKey(entityId, fields, plan, selectionSignature) {
|
|
3368
|
+
const args = [...plan.args.entries()].map(([path, entry]) => `${path}:${entry.hash}`).sort().join("|");
|
|
3369
|
+
return `${this.pendingKey(entityId, new Set(fields))}|${args}|${selectionSignature}`;
|
|
3370
|
+
}
|
|
3371
|
+
deferredSelectionSignature(selection) {
|
|
3372
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3373
|
+
const serialize = (value) => {
|
|
3374
|
+
if (value === null || typeof value !== "object") return `${typeof value}:${String(value)}`;
|
|
3375
|
+
if (seen.has(value)) return "[Circular]";
|
|
3376
|
+
seen.add(value);
|
|
3377
|
+
if (Array.isArray(value)) return `[${value.map((item) => serialize(item)).join(",")}]`;
|
|
3378
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => isViewTag(key) ? `view:${key}` : `${key}:${serialize(entry)}`).join(",")}}`;
|
|
3379
|
+
};
|
|
3380
|
+
return serialize(selection);
|
|
3381
|
+
}
|
|
3382
|
+
hasMissingDeferredList(owner, type, field, plan) {
|
|
3383
|
+
const descriptor = this.types.get(type)?.fields?.[field];
|
|
3384
|
+
if (!descriptor || descriptor === "scalar" || !("listOf" in descriptor)) return false;
|
|
3385
|
+
const fieldArgs = plan.args.get(field);
|
|
3386
|
+
const listState = this.store.getListState(getListKey(owner, field, fieldArgs?.hash));
|
|
3387
|
+
if (!listState) return true;
|
|
3388
|
+
const args = fieldArgs?.value;
|
|
3389
|
+
const paginationArgs = getPaginationArgsInfo(args);
|
|
3390
|
+
if (paginationArgs.hasCursorArg) return true;
|
|
3391
|
+
return !hasConnectionPageCoverage(listState, args, paginationArgs.direction);
|
|
3392
|
+
}
|
|
2865
3393
|
liveSubscriptionKey(entityId, plan) {
|
|
2866
3394
|
return `${entityId}|${[...plan.paths].sort().join("|")}|${[...plan.args.entries()].map(([path, entry]) => `${path}:${entry.hash}`).sort().join("|")}`;
|
|
2867
3395
|
}
|
|
@@ -3396,4 +3924,4 @@ function clientRoot(type) {
|
|
|
3396
3924
|
});
|
|
3397
3925
|
}
|
|
3398
3926
|
//#endregion
|
|
3399
|
-
export { ConnectionTag, FateClient, clientRoot, createClient, createHTTPTransport, createTRPCTransport, getListEntries, getSelectionPlan, isRecord, isViewTag, liveConnectionTopic, liveEntityTopic, liveGlobalConnectionTopic, mutation, toEntityId, view };
|
|
3927
|
+
export { ConnectionTag, DeferTag, DeferredTag, FateClient, clientRoot, createClient, createGraphQLTransport, createHTTPTransport, createTRPCTransport, defer, getDeferredMetadata, getListEntries, getSelectionPlan, graphqlMutation, isDeferred, isRecord, isViewTag, liveConnectionTopic, liveEntityTopic, liveGlobalConnectionTopic, mutation, toEntityId, view };
|