@nkzw/fate 1.3.3 → 1.5.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 +326 -0
- package/docs/api/classes/FateClient.md +51 -199
- package/docs/api/functions/clientRoot.md +1 -1
- package/docs/api/functions/createClient.md +1 -1
- package/docs/api/functions/createGraphQLTransport.md +1 -1
- package/docs/api/functions/createHTTPTransport.md +1 -1
- package/docs/api/functions/createTRPCTransport.md +5 -1
- package/docs/api/functions/defer.md +1 -1
- package/docs/api/functions/getDeferredMetadata.md +1 -1
- package/docs/api/functions/getListEntries.md +1 -1
- package/docs/api/functions/getSelectionPlan.md +1 -1
- package/docs/api/functions/graphqlMutation.md +1 -1
- package/docs/api/functions/isDeferred.md +1 -1
- 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 +4 -1
- package/docs/api/interfaces/FateThenable.md +3 -3
- package/docs/api/interfaces/Persistence.md +23 -0
- package/docs/api/interfaces/PersistenceSession.md +109 -0
- package/docs/api/interfaces/Transport.md +46 -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 +1 -1
- package/docs/api/type-aliases/DeferredMetadata.md +1 -1
- package/docs/api/type-aliases/DeferredSelection.md +1 -1
- package/docs/api/type-aliases/DeferredSnapshot.md +1 -1
- 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 +1 -1
- 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 +2 -2
- 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 +1 -1
- package/docs/api/type-aliases/GraphQLMutationInput.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationMap.md +1 -1
- package/docs/api/type-aliases/GraphQLMutationOutput.md +1 -1
- package/docs/api/type-aliases/GraphQLTransportOptions.md +19 -11
- package/docs/api/type-aliases/HydrateOptions.md +1 -1
- package/docs/api/type-aliases/HydrationLimits.md +1 -1
- 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/MutationIdentity.md +7 -0
- 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/PersistenceSnapshot.md +5 -0
- 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 +2 -2
- package/docs/api/type-aliases/RequestResult.md +1 -1
- package/docs/api/type-aliases/Selection.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 +1 -1
- package/docs/api/variables/DeferredTag.md +1 -1
- package/docs/guide/getting-started.md +2 -0
- package/docs/guide/persistence.md +323 -0
- package/lib/{liveTopics-uVP0LG-Q.mjs → args-CRLIRl_v.mjs} +1 -59
- package/lib/cli.d.mts +1 -1
- package/lib/{executor-C8HNogYG.d.mts → executor-DoiTBiVv.d.mts} +18 -65
- package/lib/{graphqlTransport-DskuXeUP.d.mts → graphqlTransport-DGLYyRxL.d.mts} +4 -14
- package/lib/{graphqlTransport-DSBVS2Fq.mjs → graphqlTransport-DmheG3AO.mjs} +3 -2
- package/lib/graphqlTransport.d.mts +1 -1
- package/lib/graphqlTransport.mjs +1 -1
- package/lib/hydration-DPZRJgnu.mjs +273 -0
- package/lib/idempotency-types-CqA-BFpn.d.mts +15 -0
- package/lib/idempotency.d.mts +26 -0
- package/lib/idempotency.mjs +34 -0
- package/lib/index.d.mts +6 -15
- package/lib/index.mjs +560 -1774
- package/lib/{list-sI2UPhHc.d.mts → list-BRT1VtiT.d.mts} +1 -2
- package/lib/list.d.mts +1 -1
- package/lib/liveTopics-DuFkDZQm.mjs +17 -0
- package/lib/persistence.d.mts +38 -0
- package/lib/persistence.mjs +1529 -0
- package/lib/protocol-DS5sf6sd.mjs +44 -0
- package/lib/{record-CZyyeLa3.d.mts → record-DITM4zb_.d.mts} +14 -19
- package/lib/server/drizzle.d.mts +45 -114
- package/lib/server/drizzle.mjs +2 -2
- package/lib/server/prisma.d.mts +52 -127
- package/lib/server/prisma.mjs +9 -8
- package/lib/server.d.mts +4 -16
- package/lib/server.mjs +21 -35
- package/lib/sortObjectKeys-yhuitEQZ.mjs +9 -0
- package/lib/{sourceRouter-DSaADXSf.mjs → sourceRouter-51POBlEE.mjs} +9 -6
- package/lib/store-CeatPJeK.mjs +1350 -0
- package/lib/{transport-DhaVVq4P.d.mts → transport-DqjhjLfC.d.mts} +672 -517
- package/lib/vite.d.mts +0 -1
- package/lib/vite.mjs +19 -11
- package/package.json +15 -5
- package/docs/api/type-aliases/Snapshot.md +0 -7
package/lib/index.mjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { t as isRecord } from "./record-B07VwXd-.mjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as hashArgs, c as scopeArgsPayload, i as filterConnectionArgs, r as combineArgsPayload, s as resolvedArgsFromPlan, t as applyArgsPayloadToPlan } from "./args-CRLIRl_v.mjs";
|
|
3
|
+
import { _ as isNodesItem, a as createNodeRef, c as ConnectionTag, f as RootKind, g as isNodeItem, i as resolveHydrationLimits, l as DeferTag, m as ViewsTag, o as getNodeRefId, r as encodeHydrationValue, s as isNodeRef, t as decodeClientHydrationState, u as DeferredTag, v as isQueryItem, y as isViewTag } from "./hydration-DPZRJgnu.mjs";
|
|
4
|
+
import { C as isDeferredSelection, S as isDeferred, _ as view, a as mutation, b as getDeferredMetadata, c as getDeferredSelectionPlan, d as createRefWithViewNames, f as getRootViewNames, g as getViewPayloads, h as getViewNames, l as getSelectionPlan, m as toEntityId, n as getListKey, p as parseEntityId, r as mergePreservingExisting, s as wrapMutation, t as Store, u as assignViewTag, v as createDeferred, x as getDeferredSelection, y as defer } from "./store-CeatPJeK.mjs";
|
|
3
5
|
import { t as getListEntries } from "./list-_6fXYLtJ.mjs";
|
|
4
|
-
import { n as
|
|
6
|
+
import { n as errorCodeFromStatus, t as FateRequestError } from "./protocol-DS5sf6sd.mjs";
|
|
7
|
+
import { n as graphqlMutation, t as createGraphQLTransport } from "./graphqlTransport-DmheG3AO.mjs";
|
|
8
|
+
import { n as liveEntityTopic, r as liveGlobalConnectionTopic, t as liveConnectionTopic } from "./liveTopics-DuFkDZQm.mjs";
|
|
5
9
|
//#region src/cache.ts
|
|
6
10
|
var ViewDataCache = class {
|
|
7
|
-
constructor() {
|
|
11
|
+
constructor(isEnabled = () => true) {
|
|
12
|
+
this.isEnabled = isEnabled;
|
|
8
13
|
this.cache = /* @__PURE__ */ new Map();
|
|
9
14
|
this.rootDependencies = /* @__PURE__ */ new Map();
|
|
10
15
|
this.dependencyIndex = /* @__PURE__ */ new Map();
|
|
@@ -15,9 +20,11 @@ var ViewDataCache = class {
|
|
|
15
20
|
this.dependencyIndex.clear();
|
|
16
21
|
}
|
|
17
22
|
get(entityId, view, ref) {
|
|
23
|
+
if (!this.isEnabled()) return null;
|
|
18
24
|
return this.cache.get(entityId)?.get(view)?.get(ref) ?? null;
|
|
19
25
|
}
|
|
20
26
|
set(entityId, view, ref, thenable, dependencies) {
|
|
27
|
+
if (!this.isEnabled()) return;
|
|
21
28
|
let entityMap = this.cache.get(entityId);
|
|
22
29
|
if (!entityMap) {
|
|
23
30
|
entityMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -45,6 +52,7 @@ var ViewDataCache = class {
|
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
invalidate(entityId) {
|
|
55
|
+
if (!this.isEnabled()) return;
|
|
48
56
|
this.invalidateDependents(entityId, /* @__PURE__ */ new Set());
|
|
49
57
|
}
|
|
50
58
|
invalidateDependents(entityId, visited) {
|
|
@@ -69,837 +77,6 @@ var ViewDataCache = class {
|
|
|
69
77
|
}
|
|
70
78
|
};
|
|
71
79
|
//#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 = /* @__PURE__ */ new Set([
|
|
310
|
-
"backwardPageLimit",
|
|
311
|
-
"cursors",
|
|
312
|
-
"forwardPageLimit",
|
|
313
|
-
"ids",
|
|
314
|
-
"liveAfterIds",
|
|
315
|
-
"liveBeforeIds",
|
|
316
|
-
"pagination",
|
|
317
|
-
"pendingAfterIds",
|
|
318
|
-
"pendingBeforeIds"
|
|
319
|
-
]);
|
|
320
|
-
const paginationKeys = /* @__PURE__ */ new Set([
|
|
321
|
-
"hasNext",
|
|
322
|
-
"hasPrevious",
|
|
323
|
-
"nextCursor",
|
|
324
|
-
"previousCursor"
|
|
325
|
-
]);
|
|
326
|
-
const clientStateKeys = /* @__PURE__ */ new Set([
|
|
327
|
-
"rootLists",
|
|
328
|
-
"rootRequests",
|
|
329
|
-
"store"
|
|
330
|
-
]);
|
|
331
|
-
const storeStateKeys = /* @__PURE__ */ 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
|
|
354
|
-
//#region src/mask.ts
|
|
355
|
-
function emptyMask() {
|
|
356
|
-
return {
|
|
357
|
-
all: false,
|
|
358
|
-
children: /* @__PURE__ */ new Map()
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
function cloneMask(m) {
|
|
362
|
-
const clone = {
|
|
363
|
-
all: m.all,
|
|
364
|
-
children: /* @__PURE__ */ new Map()
|
|
365
|
-
};
|
|
366
|
-
for (const [key, value] of m.children) clone.children.set(key, cloneMask(value));
|
|
367
|
-
return clone;
|
|
368
|
-
}
|
|
369
|
-
function addPath(mask, path) {
|
|
370
|
-
if (mask.all) return;
|
|
371
|
-
const parts = path.split(".");
|
|
372
|
-
let current = mask;
|
|
373
|
-
for (let i = 0; i < parts.length; i++) {
|
|
374
|
-
const seg = parts[i];
|
|
375
|
-
let child = current.children.get(seg);
|
|
376
|
-
if (!child) {
|
|
377
|
-
child = emptyMask();
|
|
378
|
-
current.children.set(seg, child);
|
|
379
|
-
}
|
|
380
|
-
current = child;
|
|
381
|
-
}
|
|
382
|
-
current.all = true;
|
|
383
|
-
current.children.clear();
|
|
384
|
-
}
|
|
385
|
-
function union(into, b) {
|
|
386
|
-
if (into.all || b.all) {
|
|
387
|
-
into.all = true;
|
|
388
|
-
into.children.clear();
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
for (const [key, child] of b.children) {
|
|
392
|
-
const exist = into.children.get(key);
|
|
393
|
-
if (!exist) into.children.set(key, cloneMask(child));
|
|
394
|
-
else union(exist, child);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
function fromPaths(paths) {
|
|
398
|
-
const mask = emptyMask();
|
|
399
|
-
for (const path of paths) addPath(mask, path);
|
|
400
|
-
return mask;
|
|
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
|
-
}
|
|
414
|
-
function isCovered(mask, path) {
|
|
415
|
-
if (mask.all) return true;
|
|
416
|
-
const parts = path.split(".");
|
|
417
|
-
let current = mask;
|
|
418
|
-
for (let i = 0; i < parts.length; i++) {
|
|
419
|
-
if (!current) return false;
|
|
420
|
-
if (current.all) return true;
|
|
421
|
-
current = current.children.get(parts[i]);
|
|
422
|
-
}
|
|
423
|
-
return !!current && (current.all || current.children.size === 0);
|
|
424
|
-
}
|
|
425
|
-
function diffPaths(paths, mask) {
|
|
426
|
-
const missing = /* @__PURE__ */ new Set();
|
|
427
|
-
for (const path of paths) if (!isCovered(mask, path)) missing.add(path);
|
|
428
|
-
return missing;
|
|
429
|
-
}
|
|
430
|
-
function intersects(a, b) {
|
|
431
|
-
if (a.all || b.all) return true;
|
|
432
|
-
for (const [key, child] of a.children) {
|
|
433
|
-
const otherChild = b.children.get(key);
|
|
434
|
-
if (!otherChild) continue;
|
|
435
|
-
if (child.all || otherChild.all) return true;
|
|
436
|
-
if (intersects(child, otherChild)) return true;
|
|
437
|
-
}
|
|
438
|
-
return false;
|
|
439
|
-
}
|
|
440
|
-
//#endregion
|
|
441
|
-
//#region ../../node_modules/.pnpm/@trpc+server@11.18.0_typescript@6.0.3/node_modules/@trpc/server/dist/getErrorShape-BPSzUA7W.mjs
|
|
442
|
-
var __create = Object.create;
|
|
443
|
-
var __defProp = Object.defineProperty;
|
|
444
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
445
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
446
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
447
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
448
|
-
var __commonJS = (cb, mod) => function() {
|
|
449
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
450
|
-
};
|
|
451
|
-
var __copyProps = (to, from, except, desc) => {
|
|
452
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
453
|
-
key = keys[i];
|
|
454
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
455
|
-
get: ((k) => from[k]).bind(null, key),
|
|
456
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
return to;
|
|
460
|
-
};
|
|
461
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
462
|
-
value: mod,
|
|
463
|
-
enumerable: true
|
|
464
|
-
}) : target, mod));
|
|
465
|
-
const JSONRPC2_TO_HTTP_CODE = {
|
|
466
|
-
PARSE_ERROR: 400,
|
|
467
|
-
BAD_REQUEST: 400,
|
|
468
|
-
UNAUTHORIZED: 401,
|
|
469
|
-
PAYMENT_REQUIRED: 402,
|
|
470
|
-
FORBIDDEN: 403,
|
|
471
|
-
NOT_FOUND: 404,
|
|
472
|
-
METHOD_NOT_SUPPORTED: 405,
|
|
473
|
-
TIMEOUT: 408,
|
|
474
|
-
CONFLICT: 409,
|
|
475
|
-
PRECONDITION_FAILED: 412,
|
|
476
|
-
PAYLOAD_TOO_LARGE: 413,
|
|
477
|
-
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
478
|
-
UNPROCESSABLE_CONTENT: 422,
|
|
479
|
-
PRECONDITION_REQUIRED: 428,
|
|
480
|
-
TOO_MANY_REQUESTS: 429,
|
|
481
|
-
CLIENT_CLOSED_REQUEST: 499,
|
|
482
|
-
INTERNAL_SERVER_ERROR: 500,
|
|
483
|
-
NOT_IMPLEMENTED: 501,
|
|
484
|
-
BAD_GATEWAY: 502,
|
|
485
|
-
SERVICE_UNAVAILABLE: 503,
|
|
486
|
-
GATEWAY_TIMEOUT: 504
|
|
487
|
-
};
|
|
488
|
-
function getStatusCodeFromKey(code) {
|
|
489
|
-
var _JSONRPC2_TO_HTTP_COD;
|
|
490
|
-
return (_JSONRPC2_TO_HTTP_COD = JSONRPC2_TO_HTTP_CODE[code]) !== null && _JSONRPC2_TO_HTTP_COD !== void 0 ? _JSONRPC2_TO_HTTP_COD : 500;
|
|
491
|
-
}
|
|
492
|
-
function getHTTPStatusCodeFromError(error) {
|
|
493
|
-
return getStatusCodeFromKey(error.code);
|
|
494
|
-
}
|
|
495
|
-
var require_typeof = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js"(exports, module) {
|
|
496
|
-
function _typeof$2(o) {
|
|
497
|
-
"@babel/helpers - typeof";
|
|
498
|
-
return module.exports = _typeof$2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
499
|
-
return typeof o$1;
|
|
500
|
-
} : function(o$1) {
|
|
501
|
-
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
502
|
-
}, module.exports.__esModule = true, module.exports["default"] = module.exports, _typeof$2(o);
|
|
503
|
-
}
|
|
504
|
-
module.exports = _typeof$2, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
505
|
-
} });
|
|
506
|
-
var require_toPrimitive = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js"(exports, module) {
|
|
507
|
-
var _typeof$1 = require_typeof()["default"];
|
|
508
|
-
function toPrimitive$1(t, r) {
|
|
509
|
-
if ("object" != _typeof$1(t) || !t) return t;
|
|
510
|
-
var e = t[Symbol.toPrimitive];
|
|
511
|
-
if (void 0 !== e) {
|
|
512
|
-
var i = e.call(t, r || "default");
|
|
513
|
-
if ("object" != _typeof$1(i)) return i;
|
|
514
|
-
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
515
|
-
}
|
|
516
|
-
return ("string" === r ? String : Number)(t);
|
|
517
|
-
}
|
|
518
|
-
module.exports = toPrimitive$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
519
|
-
} });
|
|
520
|
-
var require_toPropertyKey = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js"(exports, module) {
|
|
521
|
-
var _typeof = require_typeof()["default"];
|
|
522
|
-
var toPrimitive = require_toPrimitive();
|
|
523
|
-
function toPropertyKey$1(t) {
|
|
524
|
-
var i = toPrimitive(t, "string");
|
|
525
|
-
return "symbol" == _typeof(i) ? i : i + "";
|
|
526
|
-
}
|
|
527
|
-
module.exports = toPropertyKey$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
528
|
-
} });
|
|
529
|
-
var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js"(exports, module) {
|
|
530
|
-
var toPropertyKey = require_toPropertyKey();
|
|
531
|
-
function _defineProperty(e, r, t) {
|
|
532
|
-
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
533
|
-
value: t,
|
|
534
|
-
enumerable: !0,
|
|
535
|
-
configurable: !0,
|
|
536
|
-
writable: !0
|
|
537
|
-
}) : e[r] = t, e;
|
|
538
|
-
}
|
|
539
|
-
module.exports = _defineProperty, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
540
|
-
} });
|
|
541
|
-
__toESM(__commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/objectSpread2.js"(exports, module) {
|
|
542
|
-
var defineProperty = require_defineProperty();
|
|
543
|
-
function ownKeys(e, r) {
|
|
544
|
-
var t = Object.keys(e);
|
|
545
|
-
if (Object.getOwnPropertySymbols) {
|
|
546
|
-
var o = Object.getOwnPropertySymbols(e);
|
|
547
|
-
r && (o = o.filter(function(r$1) {
|
|
548
|
-
return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
|
|
549
|
-
})), t.push.apply(t, o);
|
|
550
|
-
}
|
|
551
|
-
return t;
|
|
552
|
-
}
|
|
553
|
-
function _objectSpread2(e) {
|
|
554
|
-
for (var r = 1; r < arguments.length; r++) {
|
|
555
|
-
var t = null != arguments[r] ? arguments[r] : {};
|
|
556
|
-
r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
|
|
557
|
-
defineProperty(e, r$1, t[r$1]);
|
|
558
|
-
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
|
|
559
|
-
Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
return e;
|
|
563
|
-
}
|
|
564
|
-
module.exports = _objectSpread2, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
565
|
-
} })(), 1);
|
|
566
|
-
//#endregion
|
|
567
|
-
//#region src/view.ts
|
|
568
|
-
/**
|
|
569
|
-
* Collects all view payloads that apply to the given ref.
|
|
570
|
-
*/
|
|
571
|
-
const getViewPayloads = (view, ref) => {
|
|
572
|
-
const result = [];
|
|
573
|
-
for (const [key, value] of Object.entries(view)) if (isViewTag(key) && (!ref || ref[ViewsTag]?.has(key))) result.push(value);
|
|
574
|
-
return result;
|
|
575
|
-
};
|
|
576
|
-
/**
|
|
577
|
-
* Returns the set of view tags defined on a view composition.
|
|
578
|
-
*/
|
|
579
|
-
const getViewNames = (view) => {
|
|
580
|
-
const result = /* @__PURE__ */ new Set();
|
|
581
|
-
for (const key of Object.keys(view)) if (isViewTag(key)) result.add(key);
|
|
582
|
-
return result;
|
|
583
|
-
};
|
|
584
|
-
/**
|
|
585
|
-
* Extracts view tags from a nested selection object.
|
|
586
|
-
*/
|
|
587
|
-
const getSelectionViewNames = (selection) => {
|
|
588
|
-
return getViewNames(selection);
|
|
589
|
-
};
|
|
590
|
-
let id = 0;
|
|
591
|
-
const importMetaEnvironment = import.meta.env;
|
|
592
|
-
const isDevelopment = typeof importMetaEnvironment?.DEV === "boolean" ? importMetaEnvironment.DEV : typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : false;
|
|
593
|
-
let viewModulePath = null;
|
|
594
|
-
const getStableId = () => {
|
|
595
|
-
if (isDevelopment) try {
|
|
596
|
-
if (viewModulePath == null) viewModulePath = new URL(import.meta.url).pathname;
|
|
597
|
-
const stack = (/* @__PURE__ */ new Error()).stack?.split("\n");
|
|
598
|
-
if (stack) for (let i = 1; i < stack.length; i++) {
|
|
599
|
-
const match = stack[i].trim().match(/\(?([^()]+):(\d+):(\d+)\)?$/);
|
|
600
|
-
if (!match) continue;
|
|
601
|
-
const [, source, line, column] = match;
|
|
602
|
-
if (!source.includes(viewModulePath)) {
|
|
603
|
-
const file = source.startsWith("at ") ? source.slice(3) : source;
|
|
604
|
-
return `${/^[A-Za-z][\d+.A-Za-z-]*:/.test(file) ? new URL(file).pathname : file}:${line}:${column}`;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
} catch {}
|
|
608
|
-
return String(id++);
|
|
609
|
-
};
|
|
610
|
-
/**
|
|
611
|
-
* Creates a reusable view for an object using the declared selection.
|
|
612
|
-
*
|
|
613
|
-
* @example
|
|
614
|
-
* const PostView = view<Post>()({
|
|
615
|
-
* id: true,
|
|
616
|
-
* title: true,
|
|
617
|
-
* });
|
|
618
|
-
*/
|
|
619
|
-
function view() {
|
|
620
|
-
const viewId = getStableId();
|
|
621
|
-
return (select) => {
|
|
622
|
-
const payload = Object.freeze({
|
|
623
|
-
select,
|
|
624
|
-
[ViewKind]: true
|
|
625
|
-
});
|
|
626
|
-
const viewComposition = Object.defineProperty({}, getViewTag(viewId), {
|
|
627
|
-
configurable: false,
|
|
628
|
-
enumerable: true,
|
|
629
|
-
value: payload,
|
|
630
|
-
writable: false
|
|
631
|
-
});
|
|
632
|
-
return Object.freeze(viewComposition);
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
//#endregion
|
|
636
|
-
//#region src/ref.ts
|
|
637
|
-
/**
|
|
638
|
-
* Builds the canonical cache ID for an entity.
|
|
639
|
-
*/
|
|
640
|
-
const toEntityId = (type, rawId) => `${type}:${String(rawId)}`;
|
|
641
|
-
/**
|
|
642
|
-
* Splits a cache entity ID back into its type and raw identifier.
|
|
643
|
-
*/
|
|
644
|
-
function parseEntityId(id) {
|
|
645
|
-
const idx = id.indexOf(":");
|
|
646
|
-
return idx < 0 ? {
|
|
647
|
-
id,
|
|
648
|
-
type: ""
|
|
649
|
-
} : {
|
|
650
|
-
id: id.slice(idx + 1),
|
|
651
|
-
type: id.slice(0, idx)
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* Attaches view tags to a ref without leaking the symbol.
|
|
656
|
-
*/
|
|
657
|
-
function assignViewTag(target, value) {
|
|
658
|
-
Object.defineProperty(target, ViewsTag, {
|
|
659
|
-
configurable: false,
|
|
660
|
-
enumerable: false,
|
|
661
|
-
value,
|
|
662
|
-
writable: false
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
const getRootViewNames = (view) => {
|
|
666
|
-
const names = new Set(getViewNames(view));
|
|
667
|
-
const payloads = getViewPayloads(view, null);
|
|
668
|
-
for (const payload of payloads) for (const name of getSelectionViewNames(payload.select)) names.add(name);
|
|
669
|
-
return names;
|
|
670
|
-
};
|
|
671
|
-
function createRefWithViewNames(__typename, id, names) {
|
|
672
|
-
const ref = {
|
|
673
|
-
__typename,
|
|
674
|
-
id
|
|
675
|
-
};
|
|
676
|
-
assignViewTag(ref, names);
|
|
677
|
-
return Object.freeze(ref);
|
|
678
|
-
}
|
|
679
|
-
//#endregion
|
|
680
|
-
//#region src/selection.ts
|
|
681
|
-
const isConnectionSelection = (value) => isRecord(value.items) && "node" in value.items;
|
|
682
|
-
/**
|
|
683
|
-
* Flattens a view into a `SelectionPlan`, expanding composed views and
|
|
684
|
-
* partitioning nested args so the client can fetch exactly what is declared.
|
|
685
|
-
*/
|
|
686
|
-
const getSelectionPlan = (viewComposition, ref) => {
|
|
687
|
-
const args = /* @__PURE__ */ new Map();
|
|
688
|
-
const live = /* @__PURE__ */ new Map();
|
|
689
|
-
const paths = /* @__PURE__ */ new Set();
|
|
690
|
-
const assignArgs = (path, value, ignoreKeys) => {
|
|
691
|
-
const hash = hashArgs(value, { ignoreKeys });
|
|
692
|
-
args.set(path, {
|
|
693
|
-
hash,
|
|
694
|
-
ignoreKeys,
|
|
695
|
-
value
|
|
696
|
-
});
|
|
697
|
-
};
|
|
698
|
-
const walk = (selection, prefix, context = "default") => {
|
|
699
|
-
if (prefix === null && context !== "connection" && isConnectionSelection(selection)) {
|
|
700
|
-
if (selection.live && isRecord(selection.live)) live.set("", {
|
|
701
|
-
append: selection.live.append === "visible" ? "visible" : "edge",
|
|
702
|
-
prepend: selection.live.prepend === "visible" ? "visible" : "edge"
|
|
703
|
-
});
|
|
704
|
-
if (selection.args && isRecord(selection.args)) {
|
|
705
|
-
const clonedArgs = cloneArgs(selection.args, "args");
|
|
706
|
-
const ignoreKeys = isRecord(selection.items) && isRecord(selection.items.node) ? paginationArgKeys : void 0;
|
|
707
|
-
assignArgs("", clonedArgs, ignoreKeys);
|
|
708
|
-
}
|
|
709
|
-
const { args: _args, live: _live, ...withoutArgs } = selection;
|
|
710
|
-
walk(withoutArgs, prefix, "connection");
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
for (const [key, value] of Object.entries(selection)) {
|
|
714
|
-
const valueType = typeof value;
|
|
715
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
716
|
-
if (context === "connection") {
|
|
717
|
-
if (key === "args" || key === "live" || key === "pagination") continue;
|
|
718
|
-
if (key === "items" && isRecord(value)) {
|
|
719
|
-
if (isRecord(value.node)) walk(value.node, prefix);
|
|
720
|
-
continue;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
if (valueType === "boolean") {
|
|
724
|
-
if (value) paths.add(path);
|
|
725
|
-
continue;
|
|
726
|
-
}
|
|
727
|
-
if (isDeferredSelection(value)) continue;
|
|
728
|
-
if (isViewTag(key)) {
|
|
729
|
-
if (!ref || ref[ViewsTag] && ref[ViewsTag].has(key)) walk(value.select, prefix);
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
|
-
if (isRecord(value)) {
|
|
733
|
-
const selectionObject = value;
|
|
734
|
-
if (isConnectionSelection(selectionObject)) {
|
|
735
|
-
if (selectionObject.live && isRecord(selectionObject.live)) live.set(path, {
|
|
736
|
-
append: selectionObject.live.append === "visible" ? "visible" : "edge",
|
|
737
|
-
prepend: selectionObject.live.prepend === "visible" ? "visible" : "edge"
|
|
738
|
-
});
|
|
739
|
-
if (selectionObject.args && isRecord(selectionObject.args)) {
|
|
740
|
-
const clonedArgs = cloneArgs(selectionObject.args, path);
|
|
741
|
-
const ignoreKeys = isRecord(selectionObject.items) && isRecord(selectionObject.items.node) ? paginationArgKeys : void 0;
|
|
742
|
-
assignArgs(path, clonedArgs, ignoreKeys);
|
|
743
|
-
}
|
|
744
|
-
const { args: _ignored, live: _live, ...withoutArgs } = selectionObject;
|
|
745
|
-
walk(withoutArgs, path, "connection");
|
|
746
|
-
continue;
|
|
747
|
-
}
|
|
748
|
-
const hasArgs = selectionObject.args && isRecord(selectionObject.args);
|
|
749
|
-
let selectionWithoutArgs = selectionObject;
|
|
750
|
-
if (hasArgs) {
|
|
751
|
-
const clonedArgs = cloneArgs(selectionObject.args, path);
|
|
752
|
-
const ignoreKeys = isRecord(selectionObject.items) && isRecord(selectionObject.items?.node) ? paginationArgKeys : void 0;
|
|
753
|
-
assignArgs(path, clonedArgs, ignoreKeys);
|
|
754
|
-
const { args: _args, ...rest } = selectionObject;
|
|
755
|
-
selectionWithoutArgs = rest;
|
|
756
|
-
}
|
|
757
|
-
if (Object.keys(selectionWithoutArgs).length > 0) walk(selectionWithoutArgs, path);
|
|
758
|
-
else if (hasArgs) paths.add(path);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
};
|
|
762
|
-
const payloads = getViewPayloads(viewComposition, ref);
|
|
763
|
-
if (payloads.length === 0 && isRecord(viewComposition)) walk(viewComposition, null);
|
|
764
|
-
else for (const payload of payloads) walk(payload.select, null);
|
|
765
|
-
return {
|
|
766
|
-
args,
|
|
767
|
-
live,
|
|
768
|
-
paths
|
|
769
|
-
};
|
|
770
|
-
};
|
|
771
|
-
const getDeferredSelectionPlan = (field, selection) => {
|
|
772
|
-
return getSelectionPlan({ [field]: selection }, null);
|
|
773
|
-
};
|
|
774
|
-
//#endregion
|
|
775
|
-
//#region src/mutation.ts
|
|
776
|
-
/**
|
|
777
|
-
* Defines a mutation for a given entity type, preserving the input and output
|
|
778
|
-
* types for transports.
|
|
779
|
-
*/
|
|
780
|
-
function mutation(entity) {
|
|
781
|
-
return Object.freeze({
|
|
782
|
-
entity,
|
|
783
|
-
[MutationKind]: true
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
const collectImplicitSelectedPaths = (value) => {
|
|
787
|
-
const paths = /* @__PURE__ */ new Set();
|
|
788
|
-
const walk = (current, prefix) => {
|
|
789
|
-
if (!current || typeof current !== "object") {
|
|
790
|
-
if (prefix) paths.add(prefix);
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
if (Array.isArray(current)) {
|
|
794
|
-
if (prefix) paths.add(prefix);
|
|
795
|
-
for (const child of current) walk(child, prefix);
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
for (const [key, child] of Object.entries(current)) {
|
|
799
|
-
const next = prefix ? `${prefix}.${key}` : key;
|
|
800
|
-
paths.add(next);
|
|
801
|
-
walk(child, next);
|
|
802
|
-
}
|
|
803
|
-
};
|
|
804
|
-
walk(value, null);
|
|
805
|
-
return paths;
|
|
806
|
-
};
|
|
807
|
-
const maybeGetId = (getId, input) => {
|
|
808
|
-
try {
|
|
809
|
-
return getId(input);
|
|
810
|
-
} catch {
|
|
811
|
-
return null;
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
const emptySet$1 = /* @__PURE__ */ new Set();
|
|
815
|
-
/**
|
|
816
|
-
* Binds a mutation definition to a `FateClient`, wiring up optimistic updates,
|
|
817
|
-
* cache writes, and error handling.
|
|
818
|
-
*/
|
|
819
|
-
function wrapMutation(client, identifier) {
|
|
820
|
-
const config = client.getTypeConfig(identifier.entity);
|
|
821
|
-
return async ({ args, delete: deleteRecord, input, insert = "after", optimistic, view }) => {
|
|
822
|
-
const id = maybeGetId(config.getId, input);
|
|
823
|
-
const plan = view ? getSelectionPlan(view, null) : void 0;
|
|
824
|
-
const viewSelection = plan?.paths;
|
|
825
|
-
const optimisticRecord = optimistic ? id != null ? {
|
|
826
|
-
id,
|
|
827
|
-
...optimistic
|
|
828
|
-
} : optimistic : void 0;
|
|
829
|
-
const optimisticRecordId = optimisticRecord !== void 0 ? maybeGetId(config.getId, optimisticRecord) : null;
|
|
830
|
-
const optimisticEntityId = id != null ? toEntityId(identifier.entity, id) : optimisticRecordId != null ? toEntityId(identifier.entity, optimisticRecordId) : null;
|
|
831
|
-
const snapshots = /* @__PURE__ */ new Map();
|
|
832
|
-
const listSnapshots = deleteRecord || optimisticRecord ? /* @__PURE__ */ new Map() : void 0;
|
|
833
|
-
const optimisticSelection = optimisticRecord ? collectImplicitSelectedPaths(optimisticRecord) : void 0;
|
|
834
|
-
const selection = viewSelection || optimisticSelection ? /* @__PURE__ */ new Set([...viewSelection ? [...viewSelection] : [], ...optimisticSelection ? [...optimisticSelection] : []]) : /* @__PURE__ */ new Set();
|
|
835
|
-
if (deleteRecord && id == null) throw new Error(`fate: Mutation '${identifier.key}' requires an 'id' to delete.`);
|
|
836
|
-
const optimisticToken = optimisticEntityId ? client.registerOptimisticUpdate(optimisticEntityId, optimisticSelection ?? emptySet$1) : null;
|
|
837
|
-
if (optimisticRecord && optimisticEntityId) client.write(identifier.entity, optimisticRecord, optimisticSelection ?? emptySet$1, snapshots, plan, null, null, insert, listSnapshots);
|
|
838
|
-
if (deleteRecord && id != null) client.deleteRecord(identifier.entity, id, snapshots, listSnapshots);
|
|
839
|
-
const performMutation = async () => {
|
|
840
|
-
try {
|
|
841
|
-
const result = await client.executeMutation(identifier.key, input, selection, {
|
|
842
|
-
args,
|
|
843
|
-
plan
|
|
844
|
-
});
|
|
845
|
-
if (result && typeof result === "object" && (!deleteRecord || Boolean(view))) {
|
|
846
|
-
const select = collectImplicitSelectedPaths(result);
|
|
847
|
-
const pendingMask = optimisticEntityId ? client.getPendingOptimisticMask(optimisticEntityId, { excludeToken: optimisticToken }) : null;
|
|
848
|
-
const filteredSelection = optimisticEntityId ? client.filterSelectionForPendingOptimistics(optimisticEntityId, select, { excludeToken: optimisticToken }) : select;
|
|
849
|
-
client.write(identifier.entity, result, filteredSelection, void 0, plan, null, pendingMask, insert);
|
|
850
|
-
if (deleteRecord && id != null) client.deleteRecord(identifier.entity, id);
|
|
851
|
-
const resultId = maybeGetId(config.getId, result);
|
|
852
|
-
if (optimisticEntityId && resultId != null) client.resolveOptimisticEntity(optimisticEntityId, toEntityId(identifier.entity, resultId));
|
|
853
|
-
if (optimisticRecordId != null && resultId != null && optimisticRecordId !== resultId) client.deleteRecord(identifier.entity, optimisticRecordId);
|
|
854
|
-
}
|
|
855
|
-
return {
|
|
856
|
-
error: void 0,
|
|
857
|
-
result
|
|
858
|
-
};
|
|
859
|
-
} catch (error) {
|
|
860
|
-
client.clearOptimisticUpdate(optimisticToken);
|
|
861
|
-
if (snapshots.size > 0) for (const [id, snapshot] of snapshots) client.restore(id, snapshot);
|
|
862
|
-
if (listSnapshots && listSnapshots.size > 0) for (const [name, list] of listSnapshots) client.restoreList(name, list);
|
|
863
|
-
if (error instanceof Error) {
|
|
864
|
-
const statusCode = getErrorStatusCode(error);
|
|
865
|
-
if ((statusCode ? categorizeHTTPErrorStatus(statusCode) : "boundary") === "boundary") throw error;
|
|
866
|
-
return {
|
|
867
|
-
error,
|
|
868
|
-
result: void 0
|
|
869
|
-
};
|
|
870
|
-
} else throw new Error(`fate: Mutation '${identifier.key}' failed.`);
|
|
871
|
-
} finally {
|
|
872
|
-
client.clearOptimisticUpdate(optimisticToken);
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
const mutationPromise = client.trackPendingRequest(performMutation);
|
|
876
|
-
if (optimisticEntityId) client.registerPendingOptimisticMutation(optimisticEntityId, mutationPromise);
|
|
877
|
-
return mutationPromise;
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
const getErrorStatusCode = (error) => {
|
|
881
|
-
if (error instanceof FateRequestError) return error.status;
|
|
882
|
-
const { data } = error;
|
|
883
|
-
if (!data || typeof data !== "object") return;
|
|
884
|
-
return getHTTPStatusCodeFromError(data);
|
|
885
|
-
};
|
|
886
|
-
const categorizeHTTPErrorStatus = (statusCode) => {
|
|
887
|
-
switch (statusCode) {
|
|
888
|
-
case 400:
|
|
889
|
-
case 402:
|
|
890
|
-
case 404:
|
|
891
|
-
case 408:
|
|
892
|
-
case 409:
|
|
893
|
-
case 412:
|
|
894
|
-
case 413:
|
|
895
|
-
case 415:
|
|
896
|
-
case 422:
|
|
897
|
-
case 429:
|
|
898
|
-
case 499: return "callSite";
|
|
899
|
-
default: return "boundary";
|
|
900
|
-
}
|
|
901
|
-
};
|
|
902
|
-
//#endregion
|
|
903
80
|
//#region src/operation-lifetime.ts
|
|
904
81
|
var OperationLifetime = class {
|
|
905
82
|
constructor(releaseBufferSize) {
|
|
@@ -928,6 +105,15 @@ var OperationLifetime = class {
|
|
|
928
105
|
getDescriptors() {
|
|
929
106
|
return [...this.operations.values()].map((operation) => operation.descriptor);
|
|
930
107
|
}
|
|
108
|
+
restore(descriptors) {
|
|
109
|
+
for (const descriptor of descriptors.slice(-Math.max(1, this.releaseBufferSize))) if (!this.operations.has(descriptor.key)) {
|
|
110
|
+
this.operations.set(descriptor.key, {
|
|
111
|
+
descriptor,
|
|
112
|
+
refCount: 0
|
|
113
|
+
});
|
|
114
|
+
this.releaseBuffer.push(descriptor.key);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
931
117
|
release(key) {
|
|
932
118
|
const operation = this.operations.get(key);
|
|
933
119
|
if (!operation) return false;
|
|
@@ -956,554 +142,6 @@ var OperationLifetime = class {
|
|
|
956
142
|
}
|
|
957
143
|
};
|
|
958
144
|
//#endregion
|
|
959
|
-
//#region src/store.ts
|
|
960
|
-
const listKeySeparator = " __fate__ ";
|
|
961
|
-
const decodeListKeyPart = (part) => {
|
|
962
|
-
try {
|
|
963
|
-
return decodeURIComponent(part);
|
|
964
|
-
} catch {
|
|
965
|
-
return part;
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
const encodeListKeyPart = (part) => encodeURIComponent(part);
|
|
969
|
-
const getListKey = (ownerId, field, hash = "default") => `${encodeListKeyPart(ownerId)}${listKeySeparator}${encodeListKeyPart(field)}${listKeySeparator}${encodeListKeyPart(hash)}`;
|
|
970
|
-
const getOwnerFieldKey = (ownerId, field) => JSON.stringify([ownerId, field]);
|
|
971
|
-
const parseListKey = (key) => {
|
|
972
|
-
const parts = key.split(listKeySeparator);
|
|
973
|
-
if (parts.length !== 3) return null;
|
|
974
|
-
const [ownerId, field] = parts;
|
|
975
|
-
return {
|
|
976
|
-
field: decodeListKeyPart(field),
|
|
977
|
-
ownerId: decodeListKeyPart(ownerId)
|
|
978
|
-
};
|
|
979
|
-
};
|
|
980
|
-
const cloneValue = (value) => {
|
|
981
|
-
if (Array.isArray(value)) return value.map(cloneValue);
|
|
982
|
-
if (isNodeRef(value)) return value;
|
|
983
|
-
if (value != null && typeof value === "object") {
|
|
984
|
-
const result = {};
|
|
985
|
-
for (const [key, record] of Object.entries(value)) result[key] = cloneValue(record);
|
|
986
|
-
return result;
|
|
987
|
-
}
|
|
988
|
-
return value;
|
|
989
|
-
};
|
|
990
|
-
const isPlainRecord = (value) => {
|
|
991
|
-
if (value == null || typeof value !== "object" || Array.isArray(value) || isNodeRef(value)) return false;
|
|
992
|
-
const prototype = Object.getPrototypeOf(value);
|
|
993
|
-
return prototype === Object.prototype || prototype === null;
|
|
994
|
-
};
|
|
995
|
-
const isDate = (value) => value != null && typeof value === "object" && Object.prototype.toString.call(value) === "[object Date]" && typeof value.getTime === "function";
|
|
996
|
-
const mergePreservingExisting = (incoming, existing) => {
|
|
997
|
-
if (!isPlainRecord(incoming) || !isPlainRecord(existing)) return existing;
|
|
998
|
-
const result = Object.create(null);
|
|
999
|
-
for (const [key, value] of Object.entries(incoming)) Object.defineProperty(result, key, {
|
|
1000
|
-
configurable: true,
|
|
1001
|
-
enumerable: true,
|
|
1002
|
-
value,
|
|
1003
|
-
writable: true
|
|
1004
|
-
});
|
|
1005
|
-
for (const [key, value] of Object.entries(existing)) Object.defineProperty(result, key, {
|
|
1006
|
-
configurable: true,
|
|
1007
|
-
enumerable: true,
|
|
1008
|
-
value: Object.hasOwn(result, key) ? mergePreservingExisting(result[key], value) : value,
|
|
1009
|
-
writable: true
|
|
1010
|
-
});
|
|
1011
|
-
return result;
|
|
1012
|
-
};
|
|
1013
|
-
const areHydrationValuesEqual = (left, right) => {
|
|
1014
|
-
if (Object.is(left, right)) return true;
|
|
1015
|
-
if (isNodeRef(left) || isNodeRef(right)) return isNodeRef(left) && isNodeRef(right) && getNodeRefId(left) === getNodeRefId(right);
|
|
1016
|
-
if (isDate(left) || isDate(right)) return isDate(left) && isDate(right) && left.getTime() === right.getTime();
|
|
1017
|
-
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]));
|
|
1018
|
-
if (!isPlainRecord(left) || !isPlainRecord(right)) return false;
|
|
1019
|
-
const leftKeys = Object.keys(left);
|
|
1020
|
-
const rightKeys = Object.keys(right);
|
|
1021
|
-
return leftKeys.length === rightKeys.length && leftKeys.every((key) => Object.hasOwn(right, key) && areHydrationValuesEqual(left[key], right[key]));
|
|
1022
|
-
};
|
|
1023
|
-
const areMasksEqual = (left, right) => {
|
|
1024
|
-
if (!left || !right) return left === right;
|
|
1025
|
-
const leftPaths = toPaths(left).sort();
|
|
1026
|
-
const rightPaths = toPaths(right).sort();
|
|
1027
|
-
return leftPaths.length === rightPaths.length && leftPaths.every((path, index) => path === rightPaths[index]);
|
|
1028
|
-
};
|
|
1029
|
-
const emptyFunction = () => {};
|
|
1030
|
-
var Store = class {
|
|
1031
|
-
constructor() {
|
|
1032
|
-
this.coverage = /* @__PURE__ */ new Map();
|
|
1033
|
-
this.listKeysByOwnerField = /* @__PURE__ */ new Map();
|
|
1034
|
-
this.listKeysByReferencedEntity = /* @__PURE__ */ new Map();
|
|
1035
|
-
this.lists = /* @__PURE__ */ new Map();
|
|
1036
|
-
this.recordReferenceFields = /* @__PURE__ */ new Map();
|
|
1037
|
-
this.recordReferencesByTarget = /* @__PURE__ */ new Map();
|
|
1038
|
-
this.records = /* @__PURE__ */ new Map();
|
|
1039
|
-
this.subscriptions = /* @__PURE__ */ new Map();
|
|
1040
|
-
this.listSubscriptions = /* @__PURE__ */ new Map();
|
|
1041
|
-
}
|
|
1042
|
-
dehydrate() {
|
|
1043
|
-
return {
|
|
1044
|
-
coverage: [...this.coverage].map(([id, mask]) => [id, toPaths(mask)]),
|
|
1045
|
-
lists: [...this.lists],
|
|
1046
|
-
records: [...this.records]
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
hydrate(state, mode, options = {}) {
|
|
1050
|
-
const previousCoverage = this.coverage;
|
|
1051
|
-
const previousLists = this.lists;
|
|
1052
|
-
const previousRecords = this.records;
|
|
1053
|
-
const coverage = mode === "replace" ? /* @__PURE__ */ new Map() : new Map([...previousCoverage].map(([id, mask]) => [id, cloneMask(mask)]));
|
|
1054
|
-
const lists = mode === "replace" ? /* @__PURE__ */ new Map() : new Map(previousLists);
|
|
1055
|
-
const records = mode === "replace" ? /* @__PURE__ */ new Map() : new Map(previousRecords);
|
|
1056
|
-
for (const [id, incoming] of state.records) {
|
|
1057
|
-
const previous = records.get(id);
|
|
1058
|
-
const next = previous && mode === "preserve-existing" ? mergePreservingExisting(incoming, previous) : incoming;
|
|
1059
|
-
records.set(id, previous && areHydrationValuesEqual(previous, next) ? previous : next);
|
|
1060
|
-
}
|
|
1061
|
-
for (const [id, paths] of state.coverage) {
|
|
1062
|
-
const incoming = fromPaths(paths);
|
|
1063
|
-
const previous = coverage.get(id);
|
|
1064
|
-
if (previous && mode === "preserve-existing") union(previous, incoming);
|
|
1065
|
-
else coverage.set(id, incoming);
|
|
1066
|
-
}
|
|
1067
|
-
for (const [key, list] of state.lists) {
|
|
1068
|
-
const previous = lists.get(key);
|
|
1069
|
-
if (mode === "replace" || !previous) lists.set(key, previous && areHydrationValuesEqual(previous, list) ? previous : list);
|
|
1070
|
-
}
|
|
1071
|
-
this.coverage = coverage;
|
|
1072
|
-
this.lists = lists;
|
|
1073
|
-
this.records = records;
|
|
1074
|
-
this.rebuildIndexes();
|
|
1075
|
-
const changedRecordIds = /* @__PURE__ */ new Set();
|
|
1076
|
-
for (const id of /* @__PURE__ */ new Set([...previousRecords.keys(), ...records.keys()])) if (!areHydrationValuesEqual(previousRecords.get(id), records.get(id)) || !areMasksEqual(previousCoverage.get(id), coverage.get(id))) changedRecordIds.add(id);
|
|
1077
|
-
const changedListKeys = /* @__PURE__ */ new Set();
|
|
1078
|
-
for (const key of /* @__PURE__ */ new Set([...previousLists.keys(), ...lists.keys()])) if (!areHydrationValuesEqual(previousLists.get(key), lists.get(key))) changedListKeys.add(key);
|
|
1079
|
-
const notify = () => {
|
|
1080
|
-
for (const id of changedRecordIds) this.notify(id);
|
|
1081
|
-
for (const key of changedListKeys) this.notifyListSubscribers(key);
|
|
1082
|
-
};
|
|
1083
|
-
if (!options.deferNotifications) notify();
|
|
1084
|
-
return notify;
|
|
1085
|
-
}
|
|
1086
|
-
rebuildIndexes() {
|
|
1087
|
-
this.listKeysByOwnerField.clear();
|
|
1088
|
-
this.listKeysByReferencedEntity.clear();
|
|
1089
|
-
this.recordReferenceFields.clear();
|
|
1090
|
-
this.recordReferencesByTarget.clear();
|
|
1091
|
-
for (const [id, record] of this.records) this.addRecordReferenceIndexes(id, record);
|
|
1092
|
-
for (const [key, list] of this.lists) this.addListIndexes(key, list);
|
|
1093
|
-
}
|
|
1094
|
-
read(id) {
|
|
1095
|
-
return this.records.get(id);
|
|
1096
|
-
}
|
|
1097
|
-
merge(id, partial, paths) {
|
|
1098
|
-
const changedPaths = this.mergeInternal(id, partial, paths);
|
|
1099
|
-
if (changedPaths) this.notify(id, changedPaths);
|
|
1100
|
-
}
|
|
1101
|
-
mergeInternal(id, partial, paths) {
|
|
1102
|
-
const previous = this.records.get(id);
|
|
1103
|
-
const changedPaths = /* @__PURE__ */ new Set();
|
|
1104
|
-
let mask = this.coverage.get(id);
|
|
1105
|
-
if (!mask) {
|
|
1106
|
-
mask = emptyMask();
|
|
1107
|
-
this.coverage.set(id, mask);
|
|
1108
|
-
}
|
|
1109
|
-
union(mask, fromPaths(paths));
|
|
1110
|
-
if (previous) {
|
|
1111
|
-
let hasChanges = false;
|
|
1112
|
-
for (const [key, value] of Object.entries(partial)) if (previous[key] !== value) {
|
|
1113
|
-
hasChanges = true;
|
|
1114
|
-
changedPaths.add(key);
|
|
1115
|
-
}
|
|
1116
|
-
if (!hasChanges) return null;
|
|
1117
|
-
const nextRecord = {
|
|
1118
|
-
...previous,
|
|
1119
|
-
...partial
|
|
1120
|
-
};
|
|
1121
|
-
this.records.set(id, nextRecord);
|
|
1122
|
-
this.updateRecordReferenceIndexes(id, previous, nextRecord, changedPaths);
|
|
1123
|
-
} else {
|
|
1124
|
-
const nextRecord = { ...partial };
|
|
1125
|
-
this.records.set(id, nextRecord);
|
|
1126
|
-
this.updateRecordReferenceIndexes(id, void 0, nextRecord, Object.keys(nextRecord));
|
|
1127
|
-
}
|
|
1128
|
-
return changedPaths;
|
|
1129
|
-
}
|
|
1130
|
-
deleteRecord(id) {
|
|
1131
|
-
const record = this.records.get(id);
|
|
1132
|
-
if (record) this.removeRecordReferenceIndexes(id, record);
|
|
1133
|
-
this.records.delete(id);
|
|
1134
|
-
this.coverage.delete(id);
|
|
1135
|
-
}
|
|
1136
|
-
missingForSelection(id, paths) {
|
|
1137
|
-
const requested = new Set(paths);
|
|
1138
|
-
if (!this.records.has(id)) return requested;
|
|
1139
|
-
const mask = this.coverage.get(id);
|
|
1140
|
-
if (!mask) return requested;
|
|
1141
|
-
return diffPaths(requested, mask);
|
|
1142
|
-
}
|
|
1143
|
-
subscribe(id, selectionOrFn, callback) {
|
|
1144
|
-
let mask = null;
|
|
1145
|
-
let fn = emptyFunction;
|
|
1146
|
-
if (typeof selectionOrFn === "function") fn = selectionOrFn;
|
|
1147
|
-
else if (callback) {
|
|
1148
|
-
mask = selectionOrFn ? fromPaths(selectionOrFn) : null;
|
|
1149
|
-
fn = callback;
|
|
1150
|
-
}
|
|
1151
|
-
let subscribers = this.subscriptions.get(id);
|
|
1152
|
-
if (!subscribers) {
|
|
1153
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
1154
|
-
this.subscriptions.set(id, subscribers);
|
|
1155
|
-
}
|
|
1156
|
-
const subscription = {
|
|
1157
|
-
fn,
|
|
1158
|
-
mask
|
|
1159
|
-
};
|
|
1160
|
-
subscribers.add(subscription);
|
|
1161
|
-
return () => {
|
|
1162
|
-
const set = this.subscriptions.get(id);
|
|
1163
|
-
if (!set) return;
|
|
1164
|
-
set.delete(subscription);
|
|
1165
|
-
if (set.size === 0) this.subscriptions.delete(id);
|
|
1166
|
-
};
|
|
1167
|
-
}
|
|
1168
|
-
notify(id, paths) {
|
|
1169
|
-
const set = this.subscriptions.get(id);
|
|
1170
|
-
if (!set) return;
|
|
1171
|
-
const changedPaths = paths ? [...paths] : [];
|
|
1172
|
-
const changedMask = changedPaths.length > 0 ? fromPaths(changedPaths) : null;
|
|
1173
|
-
for (const { fn, mask } of set) {
|
|
1174
|
-
if (mask && changedMask && !intersects(changedMask, mask)) continue;
|
|
1175
|
-
try {
|
|
1176
|
-
fn();
|
|
1177
|
-
} catch {}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
notifyListSubscribers(key) {
|
|
1181
|
-
const set = this.listSubscriptions.get(key);
|
|
1182
|
-
if (!set) return;
|
|
1183
|
-
for (const fn of set) try {
|
|
1184
|
-
fn();
|
|
1185
|
-
} catch {}
|
|
1186
|
-
}
|
|
1187
|
-
getList(key) {
|
|
1188
|
-
return this.lists.get(key)?.ids;
|
|
1189
|
-
}
|
|
1190
|
-
getListState(key) {
|
|
1191
|
-
return this.lists.get(key);
|
|
1192
|
-
}
|
|
1193
|
-
getListsForField(ownerId, field) {
|
|
1194
|
-
const entries = [];
|
|
1195
|
-
const keys = this.listKeysByOwnerField.get(getOwnerFieldKey(ownerId, field));
|
|
1196
|
-
if (!keys) return entries;
|
|
1197
|
-
for (const key of keys) {
|
|
1198
|
-
const list = this.lists.get(key);
|
|
1199
|
-
if (list) entries.push([key, list]);
|
|
1200
|
-
}
|
|
1201
|
-
return entries;
|
|
1202
|
-
}
|
|
1203
|
-
setList(key, state) {
|
|
1204
|
-
const previous = this.lists.get(key);
|
|
1205
|
-
if (previous) this.removeListIndexes(key, previous);
|
|
1206
|
-
this.lists.set(key, state);
|
|
1207
|
-
this.addListIndexes(key, state);
|
|
1208
|
-
this.notifyListSubscribers(key);
|
|
1209
|
-
}
|
|
1210
|
-
replaceListEntityId(previousId, nextId) {
|
|
1211
|
-
const keys = [...this.listKeysByReferencedEntity.get(previousId) ?? []];
|
|
1212
|
-
for (const key of keys) {
|
|
1213
|
-
const list = this.lists.get(key);
|
|
1214
|
-
if (!list) continue;
|
|
1215
|
-
let changed = false;
|
|
1216
|
-
let ids = list.ids;
|
|
1217
|
-
let cursors = list.cursors;
|
|
1218
|
-
if (list.ids.includes(previousId)) {
|
|
1219
|
-
const nextIds = [];
|
|
1220
|
-
const nextCursors = list.cursors ? [] : void 0;
|
|
1221
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
1222
|
-
list.ids.forEach((id, index) => {
|
|
1223
|
-
const resolved = id === previousId ? nextId : id;
|
|
1224
|
-
if (seenIds.has(resolved)) return;
|
|
1225
|
-
seenIds.add(resolved);
|
|
1226
|
-
nextIds.push(resolved);
|
|
1227
|
-
if (nextCursors) nextCursors.push(list.cursors?.[index]);
|
|
1228
|
-
});
|
|
1229
|
-
changed = true;
|
|
1230
|
-
ids = nextIds;
|
|
1231
|
-
cursors = nextCursors;
|
|
1232
|
-
}
|
|
1233
|
-
const dedupe = (values) => {
|
|
1234
|
-
if (!values || !values.includes(previousId)) return;
|
|
1235
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1236
|
-
const next = [];
|
|
1237
|
-
for (const value of values) {
|
|
1238
|
-
const resolved = value === previousId ? nextId : value;
|
|
1239
|
-
if (seen.has(resolved)) continue;
|
|
1240
|
-
seen.add(resolved);
|
|
1241
|
-
next.push(resolved);
|
|
1242
|
-
}
|
|
1243
|
-
changed = true;
|
|
1244
|
-
return next;
|
|
1245
|
-
};
|
|
1246
|
-
const pendingBeforeIds = dedupe(list.pendingBeforeIds) ?? list.pendingBeforeIds;
|
|
1247
|
-
const pendingAfterIds = dedupe(list.pendingAfterIds) ?? list.pendingAfterIds;
|
|
1248
|
-
const liveBeforeIds = dedupe(list.liveBeforeIds) ?? list.liveBeforeIds;
|
|
1249
|
-
const liveAfterIds = dedupe(list.liveAfterIds) ?? list.liveAfterIds;
|
|
1250
|
-
if (!changed) continue;
|
|
1251
|
-
const canonicalIds = new Set(ids);
|
|
1252
|
-
this.setList(key, {
|
|
1253
|
-
backwardPageLimit: list.backwardPageLimit,
|
|
1254
|
-
cursors,
|
|
1255
|
-
forwardPageLimit: list.forwardPageLimit,
|
|
1256
|
-
ids,
|
|
1257
|
-
liveAfterIds: liveAfterIds?.filter((id) => !canonicalIds.has(id)),
|
|
1258
|
-
liveBeforeIds: liveBeforeIds?.filter((id) => !canonicalIds.has(id)),
|
|
1259
|
-
pagination: list.pagination,
|
|
1260
|
-
pendingAfterIds: pendingAfterIds?.filter((id) => !canonicalIds.has(id)),
|
|
1261
|
-
pendingBeforeIds: pendingBeforeIds?.filter((id) => !canonicalIds.has(id))
|
|
1262
|
-
});
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
restoreList(key, list) {
|
|
1266
|
-
if (list == null) this.deleteList(key);
|
|
1267
|
-
else this.setList(key, list);
|
|
1268
|
-
}
|
|
1269
|
-
collectGarbage(markedRecords, markedLists, options = {}) {
|
|
1270
|
-
const records = /* @__PURE__ */ new Set();
|
|
1271
|
-
const lists = /* @__PURE__ */ new Set();
|
|
1272
|
-
for (const id of this.records.keys()) {
|
|
1273
|
-
if (markedRecords.has(id)) continue;
|
|
1274
|
-
records.add(id);
|
|
1275
|
-
}
|
|
1276
|
-
for (const key of this.lists.keys()) {
|
|
1277
|
-
if (markedLists.has(key)) continue;
|
|
1278
|
-
lists.add(key);
|
|
1279
|
-
}
|
|
1280
|
-
for (const id of records) {
|
|
1281
|
-
const record = this.records.get(id);
|
|
1282
|
-
if (record) this.removeRecordReferenceIndexes(id, record);
|
|
1283
|
-
this.records.delete(id);
|
|
1284
|
-
this.coverage.delete(id);
|
|
1285
|
-
options.onRecordDeleted?.(id);
|
|
1286
|
-
}
|
|
1287
|
-
for (const key of lists) this.deleteList(key);
|
|
1288
|
-
return {
|
|
1289
|
-
lists,
|
|
1290
|
-
records
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
deleteList(key) {
|
|
1294
|
-
const previous = this.lists.get(key);
|
|
1295
|
-
if (previous) this.removeListIndexes(key, previous);
|
|
1296
|
-
this.lists.delete(key);
|
|
1297
|
-
this.notifyListSubscribers(key);
|
|
1298
|
-
}
|
|
1299
|
-
subscribeList(key, fn) {
|
|
1300
|
-
let set = this.listSubscriptions.get(key);
|
|
1301
|
-
if (!set) {
|
|
1302
|
-
set = /* @__PURE__ */ new Set();
|
|
1303
|
-
this.listSubscriptions.set(key, set);
|
|
1304
|
-
}
|
|
1305
|
-
set.add(fn);
|
|
1306
|
-
return () => {
|
|
1307
|
-
const subscribers = this.listSubscriptions.get(key);
|
|
1308
|
-
if (!subscribers) return;
|
|
1309
|
-
subscribers.delete(fn);
|
|
1310
|
-
if (subscribers.size === 0) this.listSubscriptions.delete(key);
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
|
-
removeReferencesTo(targetId, viewDataCache, snapshots, listSnapshots) {
|
|
1314
|
-
const listKeys = [...this.listKeysByReferencedEntity.get(targetId) ?? []];
|
|
1315
|
-
for (const key of listKeys) {
|
|
1316
|
-
const list = this.lists.get(key);
|
|
1317
|
-
if (!list) continue;
|
|
1318
|
-
const { ids } = list;
|
|
1319
|
-
const hasLiveAfter = Boolean(list.liveAfterIds?.includes(targetId));
|
|
1320
|
-
const hasLiveBefore = Boolean(list.liveBeforeIds?.includes(targetId));
|
|
1321
|
-
const hasPendingAfter = Boolean(list.pendingAfterIds?.includes(targetId));
|
|
1322
|
-
const hasPendingBefore = Boolean(list.pendingBeforeIds?.includes(targetId));
|
|
1323
|
-
if (!ids.includes(targetId) && !hasLiveAfter && !hasLiveBefore && !hasPendingAfter && !hasPendingBefore) continue;
|
|
1324
|
-
if (listSnapshots && !listSnapshots.has(key)) listSnapshots.set(key, list);
|
|
1325
|
-
const entityIds = [];
|
|
1326
|
-
const cursors = list.cursors ? [] : void 0;
|
|
1327
|
-
for (let index = 0; index < ids.length; index++) {
|
|
1328
|
-
const id = ids[index];
|
|
1329
|
-
if (id === targetId) continue;
|
|
1330
|
-
entityIds.push(id);
|
|
1331
|
-
if (cursors) cursors.push(list.cursors?.[index]);
|
|
1332
|
-
}
|
|
1333
|
-
this.setList(key, {
|
|
1334
|
-
backwardPageLimit: list.backwardPageLimit,
|
|
1335
|
-
cursors,
|
|
1336
|
-
forwardPageLimit: list.forwardPageLimit,
|
|
1337
|
-
ids: entityIds,
|
|
1338
|
-
liveAfterIds: list.liveAfterIds?.filter((id) => id !== targetId),
|
|
1339
|
-
liveBeforeIds: list.liveBeforeIds?.filter((id) => id !== targetId),
|
|
1340
|
-
pagination: list.pagination,
|
|
1341
|
-
pendingAfterIds: list.pendingAfterIds?.filter((id) => id !== targetId),
|
|
1342
|
-
pendingBeforeIds: list.pendingBeforeIds?.filter((id) => id !== targetId)
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
const ids = /* @__PURE__ */ new Map();
|
|
1346
|
-
const recordEntries = [...this.recordReferencesByTarget.get(targetId)?.entries() ?? []];
|
|
1347
|
-
for (const [id, fields] of recordEntries) {
|
|
1348
|
-
const record = this.records.get(id);
|
|
1349
|
-
if (!record) continue;
|
|
1350
|
-
let updated = false;
|
|
1351
|
-
const next = {};
|
|
1352
|
-
const paths = /* @__PURE__ */ new Set();
|
|
1353
|
-
for (const key of fields) {
|
|
1354
|
-
const value = record[key];
|
|
1355
|
-
if (Array.isArray(value)) {
|
|
1356
|
-
const filtered = value.filter((item) => !(isNodeRef(item) && getNodeRefId(item) === targetId));
|
|
1357
|
-
if (filtered.length !== value.length) {
|
|
1358
|
-
updated = true;
|
|
1359
|
-
paths.add(key);
|
|
1360
|
-
next[key] = filtered;
|
|
1361
|
-
}
|
|
1362
|
-
} else if (isNodeRef(value) && getNodeRefId(value) === targetId) {
|
|
1363
|
-
updated = true;
|
|
1364
|
-
paths.add(key);
|
|
1365
|
-
next[key] = null;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
if (!updated) continue;
|
|
1369
|
-
if (snapshots && !snapshots.has(id)) snapshots.set(id, this.snapshot(id));
|
|
1370
|
-
viewDataCache.invalidate(id);
|
|
1371
|
-
this.mergeInternal(id, next, paths);
|
|
1372
|
-
ids.set(id, paths);
|
|
1373
|
-
}
|
|
1374
|
-
for (const [id, paths] of ids) this.notify(id, paths);
|
|
1375
|
-
}
|
|
1376
|
-
snapshot(id) {
|
|
1377
|
-
const record = this.records.get(id);
|
|
1378
|
-
const mask = this.coverage.get(id);
|
|
1379
|
-
return {
|
|
1380
|
-
mask: mask ? cloneMask(mask) : void 0,
|
|
1381
|
-
record: record ? cloneValue(record) : void 0
|
|
1382
|
-
};
|
|
1383
|
-
}
|
|
1384
|
-
restore(id, snapshot) {
|
|
1385
|
-
const previous = this.records.get(id);
|
|
1386
|
-
if (previous) this.removeRecordReferenceIndexes(id, previous);
|
|
1387
|
-
if (snapshot.record === void 0) this.records.delete(id);
|
|
1388
|
-
else {
|
|
1389
|
-
this.records.set(id, snapshot.record);
|
|
1390
|
-
this.addRecordReferenceIndexes(id, snapshot.record);
|
|
1391
|
-
}
|
|
1392
|
-
if (snapshot.mask === void 0) this.coverage.delete(id);
|
|
1393
|
-
else this.coverage.set(id, snapshot.mask);
|
|
1394
|
-
this.notify(id);
|
|
1395
|
-
}
|
|
1396
|
-
addListIndexes(key, list) {
|
|
1397
|
-
const parsed = parseListKey(key);
|
|
1398
|
-
if (parsed) {
|
|
1399
|
-
const ownerFieldKey = getOwnerFieldKey(parsed.ownerId, parsed.field);
|
|
1400
|
-
let keys = this.listKeysByOwnerField.get(ownerFieldKey);
|
|
1401
|
-
if (!keys) {
|
|
1402
|
-
keys = /* @__PURE__ */ new Set();
|
|
1403
|
-
this.listKeysByOwnerField.set(ownerFieldKey, keys);
|
|
1404
|
-
}
|
|
1405
|
-
keys.add(key);
|
|
1406
|
-
}
|
|
1407
|
-
for (const id of this.getListReferencedEntityIds(list)) {
|
|
1408
|
-
let keys = this.listKeysByReferencedEntity.get(id);
|
|
1409
|
-
if (!keys) {
|
|
1410
|
-
keys = /* @__PURE__ */ new Set();
|
|
1411
|
-
this.listKeysByReferencedEntity.set(id, keys);
|
|
1412
|
-
}
|
|
1413
|
-
keys.add(key);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
removeListIndexes(key, list) {
|
|
1417
|
-
const parsed = parseListKey(key);
|
|
1418
|
-
if (parsed) {
|
|
1419
|
-
const ownerFieldKey = getOwnerFieldKey(parsed.ownerId, parsed.field);
|
|
1420
|
-
const keys = this.listKeysByOwnerField.get(ownerFieldKey);
|
|
1421
|
-
if (keys) {
|
|
1422
|
-
keys.delete(key);
|
|
1423
|
-
if (keys.size === 0) this.listKeysByOwnerField.delete(ownerFieldKey);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
for (const id of this.getListReferencedEntityIds(list)) {
|
|
1427
|
-
const keys = this.listKeysByReferencedEntity.get(id);
|
|
1428
|
-
if (!keys) continue;
|
|
1429
|
-
keys.delete(key);
|
|
1430
|
-
if (keys.size === 0) this.listKeysByReferencedEntity.delete(id);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
getListReferencedEntityIds(list) {
|
|
1434
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1435
|
-
for (const id of list.ids) ids.add(id);
|
|
1436
|
-
for (const id of list.liveAfterIds ?? []) ids.add(id);
|
|
1437
|
-
for (const id of list.liveBeforeIds ?? []) ids.add(id);
|
|
1438
|
-
for (const id of list.pendingAfterIds ?? []) ids.add(id);
|
|
1439
|
-
for (const id of list.pendingBeforeIds ?? []) ids.add(id);
|
|
1440
|
-
return ids;
|
|
1441
|
-
}
|
|
1442
|
-
getRecordFieldReferences(value) {
|
|
1443
|
-
if (Array.isArray(value)) {
|
|
1444
|
-
let ids = null;
|
|
1445
|
-
for (const item of value) {
|
|
1446
|
-
if (!isNodeRef(item)) continue;
|
|
1447
|
-
if (!ids) ids = /* @__PURE__ */ new Set();
|
|
1448
|
-
ids.add(getNodeRefId(item));
|
|
1449
|
-
}
|
|
1450
|
-
return ids;
|
|
1451
|
-
}
|
|
1452
|
-
if (isNodeRef(value)) return /* @__PURE__ */ new Set([getNodeRefId(value)]);
|
|
1453
|
-
return null;
|
|
1454
|
-
}
|
|
1455
|
-
addRecordFieldReferenceIndex(id, field, value) {
|
|
1456
|
-
const targets = this.getRecordFieldReferences(value);
|
|
1457
|
-
if (!targets || targets.size === 0) return;
|
|
1458
|
-
let fields = this.recordReferenceFields.get(id);
|
|
1459
|
-
if (!fields) {
|
|
1460
|
-
fields = /* @__PURE__ */ new Map();
|
|
1461
|
-
this.recordReferenceFields.set(id, fields);
|
|
1462
|
-
}
|
|
1463
|
-
fields.set(field, targets);
|
|
1464
|
-
for (const targetId of targets) {
|
|
1465
|
-
let records = this.recordReferencesByTarget.get(targetId);
|
|
1466
|
-
if (!records) {
|
|
1467
|
-
records = /* @__PURE__ */ new Map();
|
|
1468
|
-
this.recordReferencesByTarget.set(targetId, records);
|
|
1469
|
-
}
|
|
1470
|
-
let targetFields = records.get(id);
|
|
1471
|
-
if (!targetFields) {
|
|
1472
|
-
targetFields = /* @__PURE__ */ new Set();
|
|
1473
|
-
records.set(id, targetFields);
|
|
1474
|
-
}
|
|
1475
|
-
targetFields.add(field);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
removeRecordFieldReferenceIndex(id, field) {
|
|
1479
|
-
const fields = this.recordReferenceFields.get(id);
|
|
1480
|
-
const targets = fields?.get(field);
|
|
1481
|
-
if (!fields || !targets) return;
|
|
1482
|
-
for (const targetId of targets) {
|
|
1483
|
-
const records = this.recordReferencesByTarget.get(targetId);
|
|
1484
|
-
const targetFields = records?.get(id);
|
|
1485
|
-
if (!records || !targetFields) continue;
|
|
1486
|
-
targetFields.delete(field);
|
|
1487
|
-
if (targetFields.size === 0) records.delete(id);
|
|
1488
|
-
if (records.size === 0) this.recordReferencesByTarget.delete(targetId);
|
|
1489
|
-
}
|
|
1490
|
-
fields.delete(field);
|
|
1491
|
-
if (fields.size === 0) this.recordReferenceFields.delete(id);
|
|
1492
|
-
}
|
|
1493
|
-
addRecordReferenceIndexes(id, record) {
|
|
1494
|
-
for (const [field, value] of Object.entries(record)) this.addRecordFieldReferenceIndex(id, field, value);
|
|
1495
|
-
}
|
|
1496
|
-
removeRecordReferenceIndexes(id, record) {
|
|
1497
|
-
for (const field of Object.keys(record)) this.removeRecordFieldReferenceIndex(id, field);
|
|
1498
|
-
}
|
|
1499
|
-
updateRecordReferenceIndexes(id, previous, next, fields) {
|
|
1500
|
-
for (const field of fields) {
|
|
1501
|
-
if (previous) this.removeRecordFieldReferenceIndex(id, field);
|
|
1502
|
-
this.addRecordFieldReferenceIndex(id, field, next[field]);
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
};
|
|
1506
|
-
//#endregion
|
|
1507
145
|
//#region src/request-descriptor.ts
|
|
1508
146
|
const serializeId = (value) => `${typeof value}:${String(value)}`;
|
|
1509
147
|
const getViewSignature = (view) => {
|
|
@@ -1826,24 +464,27 @@ var FateClient = class {
|
|
|
1826
464
|
this.pending = /* @__PURE__ */ new Map();
|
|
1827
465
|
this.pendingDeferred = /* @__PURE__ */ new Map();
|
|
1828
466
|
this.pendingOptimisticMutations = /* @__PURE__ */ new Map();
|
|
1829
|
-
this.optimisticMasks = /* @__PURE__ */ new Map();
|
|
1830
|
-
this.optimisticByEntity = /* @__PURE__ */ new Map();
|
|
1831
467
|
this.optimisticEntityResolutions = /* @__PURE__ */ new Map();
|
|
1832
|
-
this.optimisticTokenCounter = 0;
|
|
1833
468
|
this.liveSubscriptions = /* @__PURE__ */ new Map();
|
|
1834
469
|
this.liveConnectionInvalidations = /* @__PURE__ */ new Map();
|
|
1835
470
|
this.stableRefs = /* @__PURE__ */ new Map();
|
|
1836
471
|
this.requests = /* @__PURE__ */ new Map();
|
|
472
|
+
this.requestExecutionOptions = /* @__PURE__ */ new WeakMap();
|
|
1837
473
|
this.rootRequests = /* @__PURE__ */ new Map();
|
|
1838
474
|
this.stalledRequests = /* @__PURE__ */ new Set();
|
|
1839
475
|
this.gcPending = false;
|
|
1840
476
|
this.gcScheduled = false;
|
|
1841
477
|
this.pendingNetworkRequests = 0;
|
|
1842
|
-
this.store = new Store()
|
|
1843
|
-
|
|
478
|
+
this.store = new Store((ids) => {
|
|
479
|
+
for (const id of ids) this.viewDataCache.invalidate(id);
|
|
480
|
+
this.runPendingGarbageCollection();
|
|
481
|
+
}, (kind, key, paths, previousList) => this.persistence?.changed(kind, key, paths, previousList));
|
|
482
|
+
this.viewDataCache = new ViewDataCache(() => !this.store.isRebasing);
|
|
1844
483
|
this.liveConnectionInvalidationToken = 0;
|
|
484
|
+
this.persistenceDisposal = options.persistence ? new AbortController() : void 0;
|
|
1845
485
|
this.actions = Object.create(null);
|
|
1846
486
|
this.mutationMap = Object.create(null);
|
|
487
|
+
if (options.persistence) this.mutationEntities = new Map(Object.entries(options.mutations ?? {}).map(([key, definition]) => [key, definition.entity]));
|
|
1847
488
|
this.mutations = Object.create(null);
|
|
1848
489
|
this.onLiveError = options.onLiveError;
|
|
1849
490
|
this.operationLifetime = new OperationLifetime(options.gcReleaseBufferSize ?? 10);
|
|
@@ -1867,6 +508,7 @@ var FateClient = class {
|
|
|
1867
508
|
setNestedValue(this.actions, key, async (_previousState, data) => data === "reset" ? null : await this.mutationMap[key](data));
|
|
1868
509
|
}
|
|
1869
510
|
this.initializeParentLists();
|
|
511
|
+
this.persistence = options.persistence?.attach(this);
|
|
1870
512
|
}
|
|
1871
513
|
initializeParentLists() {
|
|
1872
514
|
for (const config of this.types.values()) {
|
|
@@ -1945,6 +587,60 @@ var FateClient = class {
|
|
|
1945
587
|
this.viewDataCache.clear();
|
|
1946
588
|
notify();
|
|
1947
589
|
}
|
|
590
|
+
/** @internal */
|
|
591
|
+
exportPersistenceState() {
|
|
592
|
+
return {
|
|
593
|
+
data: encodeHydrationValue({
|
|
594
|
+
rootLists: [...this.rootLists].map(([type, keys]) => [type, [...keys]]),
|
|
595
|
+
rootRequests: [...this.rootRequests],
|
|
596
|
+
store: this.store.dehydrateConfirmed()
|
|
597
|
+
}, this.hydrationLimits),
|
|
598
|
+
scope: this.hydrationScope,
|
|
599
|
+
version: 1
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
/** @internal */
|
|
603
|
+
retainPersistenceRequest(request) {
|
|
604
|
+
return this.operationLifetime.retain(request, () => this.scheduleGarbageCollection());
|
|
605
|
+
}
|
|
606
|
+
/** @internal */
|
|
607
|
+
getPersistenceScope() {
|
|
608
|
+
return this.hydrationScope;
|
|
609
|
+
}
|
|
610
|
+
/** @internal */
|
|
611
|
+
getPersistenceRootLists(type) {
|
|
612
|
+
return this.rootLists.get(type) ?? /* @__PURE__ */ new Set();
|
|
613
|
+
}
|
|
614
|
+
/** @internal */
|
|
615
|
+
getPersistenceQuery(key) {
|
|
616
|
+
return this.rootRequests.get(key);
|
|
617
|
+
}
|
|
618
|
+
/** @internal Merge a small disk read beneath optimistic layers. */
|
|
619
|
+
restorePersistenceData(state) {
|
|
620
|
+
this.store.update(() => {
|
|
621
|
+
const coverage = new Map(state.store.coverage);
|
|
622
|
+
for (const [id, record] of state.store.records) {
|
|
623
|
+
const existing = this.store.read(id);
|
|
624
|
+
const paths = coverage.get(id) ?? [];
|
|
625
|
+
this.store.merge(id, existing ? mergePreservingExisting(record, existing) : record, paths);
|
|
626
|
+
}
|
|
627
|
+
for (const [key, list] of state.store.lists) if (!this.store.getListState(key)) this.store.setList(key, list);
|
|
628
|
+
for (const [key, id] of state.rootRequests) if (!this.rootRequests.has(key)) this.rootRequests.set(key, id);
|
|
629
|
+
for (const [type, keys] of state.rootLists) for (const key of keys) this.registerRootList(type, key);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
/** @internal */
|
|
633
|
+
getRetainedRequests() {
|
|
634
|
+
return this.operationLifetime.getDescriptors();
|
|
635
|
+
}
|
|
636
|
+
/** @internal */
|
|
637
|
+
restoreRetainedRequests(requests) {
|
|
638
|
+
this.operationLifetime.restore(requests);
|
|
639
|
+
}
|
|
640
|
+
/** @internal */
|
|
641
|
+
hasMutation(key, entity) {
|
|
642
|
+
return this.mutationEntities?.get(key) === entity && this.types.has(entity);
|
|
643
|
+
}
|
|
1948
644
|
registerRootList(type, key) {
|
|
1949
645
|
let lists = this.rootLists.get(type);
|
|
1950
646
|
if (!lists) {
|
|
@@ -1953,11 +649,6 @@ var FateClient = class {
|
|
|
1953
649
|
}
|
|
1954
650
|
lists.add(key);
|
|
1955
651
|
}
|
|
1956
|
-
snapshotList(key, listSnapshots) {
|
|
1957
|
-
if (!listSnapshots || listSnapshots.has(key)) return;
|
|
1958
|
-
const listState = this.store.getListState(key);
|
|
1959
|
-
if (listState) listSnapshots.set(key, listState);
|
|
1960
|
-
}
|
|
1961
652
|
applyListInsert(listState, entityId, insert) {
|
|
1962
653
|
const pendingBeforeIds = listState.pendingBeforeIds ?? [];
|
|
1963
654
|
const pendingAfterIds = listState.pendingAfterIds ?? [];
|
|
@@ -1986,7 +677,7 @@ var FateClient = class {
|
|
|
1986
677
|
pending: false
|
|
1987
678
|
};
|
|
1988
679
|
}
|
|
1989
|
-
insertIntoRootLists(type, entityId, insert
|
|
680
|
+
insertIntoRootLists(type, entityId, insert) {
|
|
1990
681
|
if (insert === "none") return;
|
|
1991
682
|
const listNames = this.rootLists.get(type);
|
|
1992
683
|
if (!listNames) return;
|
|
@@ -1995,7 +686,6 @@ var FateClient = class {
|
|
|
1995
686
|
if (!listState) continue;
|
|
1996
687
|
const next = this.applyListInsert(listState, entityId, insert).list;
|
|
1997
688
|
if (next === listState) continue;
|
|
1998
|
-
this.snapshotList(key, listSnapshots);
|
|
1999
689
|
this.store.setList(key, next);
|
|
2000
690
|
}
|
|
2001
691
|
}
|
|
@@ -2009,8 +699,28 @@ var FateClient = class {
|
|
|
2009
699
|
if (!descriptor || descriptor === "scalar" || !("listOf" in descriptor)) throw new Error(`fate: Could not find node type for '${type}.${field}'.`);
|
|
2010
700
|
return descriptor.listOf;
|
|
2011
701
|
}
|
|
702
|
+
/** @internal Validates local wiring without requiring network connectivity. */
|
|
703
|
+
validatePersistence() {
|
|
704
|
+
if (this.mutationEntities?.size) this.requireDurableMutationTransport();
|
|
705
|
+
}
|
|
706
|
+
/** @internal */
|
|
707
|
+
assertPersistenceActive() {
|
|
708
|
+
if (this.persistenceDisposal?.signal.aborted || this.persistence?.getSnapshot().status === "disposed") throw new Error("fate: Persistence was disposed.");
|
|
709
|
+
}
|
|
710
|
+
/** @internal */
|
|
711
|
+
disposePersistence() {
|
|
712
|
+
this.persistenceDisposal?.abort();
|
|
713
|
+
for (const { unsubscribe } of this.liveSubscriptions.values()) try {
|
|
714
|
+
unsubscribe();
|
|
715
|
+
} catch {}
|
|
716
|
+
this.liveSubscriptions.clear();
|
|
717
|
+
}
|
|
718
|
+
requireDurableMutationTransport() {
|
|
719
|
+
if (!this.transport.mutateDurably) throw new FateRequestError("BAD_REQUEST", "fate: Persisted mutations require transport.mutateDurably connected to a server idempotency handler. Configure that adapter, or use persist: false for immediate calls. Cache persistence remains available.");
|
|
720
|
+
}
|
|
2012
721
|
async executeMutation(key, input, select, options = {}) {
|
|
2013
|
-
if (
|
|
722
|
+
if (options.identity) this.requireDurableMutationTransport();
|
|
723
|
+
else if (!this.transport.mutate) throw new Error(`fate: transport does not support mutations. Please provide a 'mutate' implementation in your transport.`);
|
|
2014
724
|
const baseRecord = input && typeof input === "object" ? input : void 0;
|
|
2015
725
|
const inputArgs = baseRecord && typeof baseRecord.args === "object" ? baseRecord.args : void 0;
|
|
2016
726
|
const argsPayload = combineArgsPayload(options.plan ? resolvedArgsFromPlan(options.plan) : void 0, combineArgsPayload(inputArgs, options.args));
|
|
@@ -2018,25 +728,19 @@ var FateClient = class {
|
|
|
2018
728
|
...baseRecord,
|
|
2019
729
|
args: argsPayload
|
|
2020
730
|
} : argsPayload ? { args: argsPayload } : input;
|
|
2021
|
-
return await this.trackPendingRequest(() => this.transport.mutate(key, requestInput, select));
|
|
2022
|
-
}
|
|
2023
|
-
write(type, data, select,
|
|
2024
|
-
return this.writeEntity(type, data, select,
|
|
2025
|
-
}
|
|
2026
|
-
deleteRecord(type, id
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
restore(id, snapshot) {
|
|
2035
|
-
this.viewDataCache.invalidate(id);
|
|
2036
|
-
this.store.restore(id, snapshot);
|
|
2037
|
-
}
|
|
2038
|
-
restoreList(name, list) {
|
|
2039
|
-
this.store.restoreList(name, list);
|
|
731
|
+
return await this.trackPendingRequest(() => options.identity ? this.transport.mutateDurably(key, requestInput, select, options.identity) : this.transport.mutate(key, requestInput, select));
|
|
732
|
+
}
|
|
733
|
+
write(type, data, select, plan, insert) {
|
|
734
|
+
return this.writeEntity(type, data, select, plan, null, insert);
|
|
735
|
+
}
|
|
736
|
+
deleteRecord(type, id) {
|
|
737
|
+
return this.store.update(() => {
|
|
738
|
+
const entityId = toEntityId(type, id);
|
|
739
|
+
this.viewDataCache.invalidate(entityId);
|
|
740
|
+
this.stableRefs.delete(entityId);
|
|
741
|
+
this.store.deleteRecord(entityId);
|
|
742
|
+
this.store.removeReferencesTo(entityId, this.viewDataCache);
|
|
743
|
+
});
|
|
2040
744
|
}
|
|
2041
745
|
stableRefWithViewNames(type, id, viewNames) {
|
|
2042
746
|
const entityId = toEntityId(type, id);
|
|
@@ -2060,6 +764,18 @@ var FateClient = class {
|
|
|
2060
764
|
return this.stableRefWithViewNames(type, id, getRootViewNames(rootView));
|
|
2061
765
|
}
|
|
2062
766
|
readView(view, ref) {
|
|
767
|
+
if (this.persistence && this.persistence.getSnapshot().status !== "ready") {
|
|
768
|
+
const key = `restore:${ref.__typename}:${ref.id}:${[...getViewNames(view)].sort().join(",")}`;
|
|
769
|
+
let pending = this.pending.get(key);
|
|
770
|
+
if (!pending) {
|
|
771
|
+
pending = this.persistence.ready.then(() => {
|
|
772
|
+
if (this.persistence?.getSnapshot().status === "disposed") throw new Error("fate: Persistence was disposed.");
|
|
773
|
+
return this.readView(view, ref);
|
|
774
|
+
}).finally(() => this.pending.delete(key));
|
|
775
|
+
this.pending.set(key, pending);
|
|
776
|
+
}
|
|
777
|
+
return pending;
|
|
778
|
+
}
|
|
2063
779
|
const id = ref.id;
|
|
2064
780
|
const type = ref.__typename;
|
|
2065
781
|
if (id == null) {
|
|
@@ -2112,7 +828,36 @@ var FateClient = class {
|
|
|
2112
828
|
const pendingPromise = this.pending.get(key) || null;
|
|
2113
829
|
if (pendingPromise) return pendingPromise;
|
|
2114
830
|
const promise = this.trackPendingRequest(async () => {
|
|
2115
|
-
|
|
831
|
+
if (this.persistence) {
|
|
832
|
+
const persistedRequest = {
|
|
833
|
+
items: [{
|
|
834
|
+
ids: [id],
|
|
835
|
+
kind: "node",
|
|
836
|
+
name: type,
|
|
837
|
+
plan,
|
|
838
|
+
refViewNames: /* @__PURE__ */ new Set(),
|
|
839
|
+
type,
|
|
840
|
+
viewSignature: ""
|
|
841
|
+
}],
|
|
842
|
+
key: `view:${key}`
|
|
843
|
+
};
|
|
844
|
+
await this.persistence.restoreRequest(persistedRequest);
|
|
845
|
+
const remaining = this.store.missingForSelection(entityId, selectedPaths);
|
|
846
|
+
if (remaining.size) {
|
|
847
|
+
await this.fetchByIdAndNormalize(type, [id], remaining, plan);
|
|
848
|
+
this.persistence.fetched({
|
|
849
|
+
...persistedRequest,
|
|
850
|
+
items: persistedRequest.items.map((item) => ({
|
|
851
|
+
...item,
|
|
852
|
+
plan: {
|
|
853
|
+
...item.plan,
|
|
854
|
+
paths: remaining
|
|
855
|
+
}
|
|
856
|
+
})),
|
|
857
|
+
key: `${persistedRequest.key}:${JSON.stringify([...remaining].sort())}`
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
} else await this.fetchByIdAndNormalize(type, [id], missing, plan);
|
|
2116
861
|
try {
|
|
2117
862
|
if (this.store.missingForSelection(entityId, selectedPaths).size > 0) {
|
|
2118
863
|
this.stalledRequests.add(key);
|
|
@@ -2193,6 +938,7 @@ var FateClient = class {
|
|
|
2193
938
|
if (!this.transport.subscribeConnection) throw new Error(`fate: transport does not support live list views. Please provide a 'subscribeConnection' implementation in your transport.`);
|
|
2194
939
|
}
|
|
2195
940
|
subscribeLiveView(view, ref) {
|
|
941
|
+
this.assertPersistenceActive();
|
|
2196
942
|
this.assertLiveViewSupport();
|
|
2197
943
|
const id = ref.id;
|
|
2198
944
|
const type = ref.__typename;
|
|
@@ -2220,21 +966,26 @@ var FateClient = class {
|
|
|
2220
966
|
try {
|
|
2221
967
|
unsubscribe = this.transport.subscribeById(type, id, plan.paths, args, {
|
|
2222
968
|
onData: (record, liveSelect) => {
|
|
2223
|
-
if (!record || typeof record !== "object") return;
|
|
2224
|
-
const pendingMask = this.getPendingOptimisticMask(entityId);
|
|
969
|
+
if (this.persistenceDisposal?.signal.aborted || !record || typeof record !== "object") return;
|
|
2225
970
|
const select = new Set(liveSelect ?? plan.paths);
|
|
2226
|
-
|
|
2227
|
-
this.write(type, record, selection, void 0, plan, null, pendingMask, "none");
|
|
971
|
+
this.write(type, record, select, plan, "none");
|
|
2228
972
|
},
|
|
2229
973
|
onDelete: (deletedId) => {
|
|
974
|
+
if (this.persistenceDisposal?.signal.aborted) return;
|
|
2230
975
|
this.deleteRecord(type, deletedId ?? id);
|
|
2231
976
|
},
|
|
2232
|
-
onError: (error) =>
|
|
977
|
+
onError: (error) => {
|
|
978
|
+
if (!this.persistenceDisposal?.signal.aborted) this.reportLiveError(error);
|
|
979
|
+
}
|
|
2233
980
|
});
|
|
2234
981
|
} catch (error) {
|
|
2235
982
|
this.reportLiveError(error);
|
|
2236
983
|
return emptyDispose;
|
|
2237
984
|
}
|
|
985
|
+
if (this.persistenceDisposal?.signal.aborted) {
|
|
986
|
+
unsubscribe();
|
|
987
|
+
return emptyDispose;
|
|
988
|
+
}
|
|
2238
989
|
this.liveSubscriptions.set(key, {
|
|
2239
990
|
count: 1,
|
|
2240
991
|
unsubscribe
|
|
@@ -2244,6 +995,7 @@ var FateClient = class {
|
|
|
2244
995
|
};
|
|
2245
996
|
}
|
|
2246
997
|
subscribeLiveListView(view, connection) {
|
|
998
|
+
this.assertPersistenceActive();
|
|
2247
999
|
this.assertLiveConnectionSupport();
|
|
2248
1000
|
const plan = getSelectionPlan(view, null);
|
|
2249
1001
|
const connectionArgs = filterConnectionArgs(connection.args);
|
|
@@ -2260,8 +1012,11 @@ var FateClient = class {
|
|
|
2260
1012
|
let unsubscribe;
|
|
2261
1013
|
try {
|
|
2262
1014
|
unsubscribe = this.transport.subscribeConnection(connection.procedure, connection.type, connectionArgs, plan.paths, selectionArgs, {
|
|
2263
|
-
onError: (error) =>
|
|
1015
|
+
onError: (error) => {
|
|
1016
|
+
if (!this.persistenceDisposal?.signal.aborted) this.reportLiveError(error);
|
|
1017
|
+
},
|
|
2264
1018
|
onEvent: (event) => {
|
|
1019
|
+
if (this.persistenceDisposal?.signal.aborted) return;
|
|
2265
1020
|
this.applyLiveConnectionEvent(view, connection, plan, event);
|
|
2266
1021
|
}
|
|
2267
1022
|
});
|
|
@@ -2269,6 +1024,10 @@ var FateClient = class {
|
|
|
2269
1024
|
this.reportLiveError(error);
|
|
2270
1025
|
return emptyDispose;
|
|
2271
1026
|
}
|
|
1027
|
+
if (this.persistenceDisposal?.signal.aborted) {
|
|
1028
|
+
unsubscribe();
|
|
1029
|
+
return emptyDispose;
|
|
1030
|
+
}
|
|
2272
1031
|
this.liveSubscriptions.set(key, {
|
|
2273
1032
|
count: 1,
|
|
2274
1033
|
unsubscribe
|
|
@@ -2278,24 +1037,26 @@ var FateClient = class {
|
|
|
2278
1037
|
};
|
|
2279
1038
|
}
|
|
2280
1039
|
applyLiveConnectionEvent(view, connection, plan, event) {
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
1040
|
+
return this.store.update(() => {
|
|
1041
|
+
if (event.type === "invalidate") {
|
|
1042
|
+
this.invalidateLiveConnection(view, connection);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
if (event.type === "deleteEdge") {
|
|
1046
|
+
this.deleteConnectionEdge(connection, toEntityId(event.nodeType, event.id));
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const node = event.edge.node;
|
|
1050
|
+
if (!node || typeof node !== "object") return;
|
|
1051
|
+
const nodeRecord = node;
|
|
1052
|
+
const nodeType = event.nodeType;
|
|
1053
|
+
if (!nodeType) {
|
|
1054
|
+
this.reportLiveError(/* @__PURE__ */ new Error(`fate: Live connection event for '${connection.procedure}' did not include a node type.`));
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
const entityId = this.write(nodeType, nodeRecord, plan.paths, plan, "none");
|
|
1058
|
+
this.insertConnectionEdge(connection.key, entityId, event.edge.cursor, event.type, event.targetCursor, view, connection);
|
|
1059
|
+
});
|
|
2299
1060
|
}
|
|
2300
1061
|
invalidateLiveConnection(view, connection) {
|
|
2301
1062
|
const token = ++this.liveConnectionInvalidationToken;
|
|
@@ -2524,11 +1285,14 @@ var FateClient = class {
|
|
|
2524
1285
|
const cursors = hasIncomingCursor || Boolean(previous?.cursors) || options.hasCursorArg ? ids.map((id) => cursorMap.get(id)) : void 0;
|
|
2525
1286
|
const previousPagination = previous?.pagination;
|
|
2526
1287
|
const newPagination = incomingPagination;
|
|
1288
|
+
const extendsExistingList = options.hasCursorArg && existingIds.length > 0;
|
|
1289
|
+
const leadingPagination = extendsExistingList && !isBackward ? previousPagination ?? newPagination : newPagination;
|
|
1290
|
+
const trailingPagination = extendsExistingList && isBackward ? previousPagination ?? newPagination : newPagination;
|
|
2527
1291
|
const pagination = previousPagination || newPagination ? {
|
|
2528
|
-
hasNext: !!(
|
|
2529
|
-
hasPrevious: !!(
|
|
2530
|
-
nextCursor:
|
|
2531
|
-
previousCursor:
|
|
1292
|
+
hasNext: !!(trailingPagination?.hasNext ?? previousPagination?.hasNext),
|
|
1293
|
+
hasPrevious: !!(leadingPagination?.hasPrevious ?? previousPagination?.hasPrevious),
|
|
1294
|
+
nextCursor: trailingPagination?.nextCursor ?? previousPagination?.nextCursor,
|
|
1295
|
+
previousCursor: leadingPagination?.previousCursor ?? previousPagination?.previousCursor
|
|
2532
1296
|
} : void 0;
|
|
2533
1297
|
return {
|
|
2534
1298
|
backwardPageLimit: !options.hasCursorArg && isBackward && options.pageLimit != null ? Math.max(previous?.backwardPageLimit ?? 0, options.pageLimit) : previous?.backwardPageLimit,
|
|
@@ -2562,60 +1326,10 @@ var FateClient = class {
|
|
|
2562
1326
|
this.optimisticEntityResolutions.set(optimisticEntityId, resolvedEntityId);
|
|
2563
1327
|
this.store.replaceListEntityId(optimisticEntityId, resolvedEntityId);
|
|
2564
1328
|
}
|
|
2565
|
-
registerOptimisticUpdate(entityId, select) {
|
|
2566
|
-
if (!entityId || select.size === 0) return null;
|
|
2567
|
-
const mask = fromPaths(select);
|
|
2568
|
-
const token = ++this.optimisticTokenCounter;
|
|
2569
|
-
this.optimisticMasks.set(token, {
|
|
2570
|
-
entityId,
|
|
2571
|
-
mask
|
|
2572
|
-
});
|
|
2573
|
-
let entries = this.optimisticByEntity.get(entityId);
|
|
2574
|
-
if (!entries) {
|
|
2575
|
-
entries = /* @__PURE__ */ new Set();
|
|
2576
|
-
this.optimisticByEntity.set(entityId, entries);
|
|
2577
|
-
}
|
|
2578
|
-
entries.add(token);
|
|
2579
|
-
return token;
|
|
2580
|
-
}
|
|
2581
|
-
clearOptimisticUpdate(token) {
|
|
2582
|
-
if (token == null) return;
|
|
2583
|
-
const entry = this.optimisticMasks.get(token);
|
|
2584
|
-
if (!entry) return;
|
|
2585
|
-
this.optimisticMasks.delete(token);
|
|
2586
|
-
const entityTokens = this.optimisticByEntity.get(entry.entityId);
|
|
2587
|
-
if (entityTokens) {
|
|
2588
|
-
entityTokens.delete(token);
|
|
2589
|
-
if (entityTokens.size === 0) this.optimisticByEntity.delete(entry.entityId);
|
|
2590
|
-
}
|
|
2591
|
-
this.runPendingGarbageCollection();
|
|
2592
|
-
}
|
|
2593
|
-
getPendingOptimisticMask(entityId, options = {}) {
|
|
2594
|
-
if (!entityId) return null;
|
|
2595
|
-
const tokens = this.optimisticByEntity.get(entityId);
|
|
2596
|
-
if (!tokens || tokens.size === 0) return null;
|
|
2597
|
-
let mask = null;
|
|
2598
|
-
for (const token of tokens) {
|
|
2599
|
-
if (options.excludeToken != null && token === options.excludeToken) continue;
|
|
2600
|
-
const entry = this.optimisticMasks.get(token);
|
|
2601
|
-
if (!entry) continue;
|
|
2602
|
-
if (!mask) mask = cloneMask(entry.mask);
|
|
2603
|
-
else union(mask, entry.mask);
|
|
2604
|
-
}
|
|
2605
|
-
return mask;
|
|
2606
|
-
}
|
|
2607
1329
|
getPendingOptimisticMutations(entityId) {
|
|
2608
1330
|
const pending = this.pendingOptimisticMutations.get(entityId);
|
|
2609
1331
|
return pending && pending.size > 0 ? [...pending] : null;
|
|
2610
1332
|
}
|
|
2611
|
-
filterSelectionForPendingOptimistics(entityId, select, options = {}) {
|
|
2612
|
-
if (!entityId || select.size === 0) return select;
|
|
2613
|
-
const pendingMask = this.getPendingOptimisticMask(entityId, options);
|
|
2614
|
-
if (!pendingMask) return select;
|
|
2615
|
-
const filtered = /* @__PURE__ */ new Set();
|
|
2616
|
-
for (const path of select) if (!isCovered(pendingMask, path)) filtered.add(path);
|
|
2617
|
-
return filtered;
|
|
2618
|
-
}
|
|
2619
1333
|
async loadConnection(view, connection, args, options = {}) {
|
|
2620
1334
|
return await this.trackPendingRequest(() => this.loadConnectionInternal(view, connection, args, options));
|
|
2621
1335
|
}
|
|
@@ -2629,22 +1343,25 @@ var FateClient = class {
|
|
|
2629
1343
|
});
|
|
2630
1344
|
const { argsPayload, plan } = resolveSelectionPlan(view, requestArgs);
|
|
2631
1345
|
const { items, pagination } = await this.transport.fetchList(connection.field, plan.paths, argsPayload);
|
|
1346
|
+
this.assertPersistenceActive();
|
|
2632
1347
|
if (!items) return this.store.getListState(connection.key);
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
1348
|
+
this.store.update(() => {
|
|
1349
|
+
const incomingIds = [];
|
|
1350
|
+
const incomingCursors = [];
|
|
1351
|
+
for (const entry of items) {
|
|
1352
|
+
const id = this.write(connection.type, entry.node, plan.paths, plan, "none");
|
|
1353
|
+
incomingIds.push(id);
|
|
1354
|
+
incomingCursors.push(entry.cursor);
|
|
1355
|
+
}
|
|
1356
|
+
const previous = this.store.getListState(connection.key);
|
|
1357
|
+
const nextListState = this.mergeListState(previous, incomingIds, incomingCursors, pagination, {
|
|
1358
|
+
...getPaginationMergeInfo(requestArgs),
|
|
1359
|
+
replace: true
|
|
1360
|
+
});
|
|
1361
|
+
if (!filterConnectionArgs(connection.args)) this.registerRootList(connection.type, connection.key);
|
|
1362
|
+
this.store.setList(connection.key, nextListState);
|
|
2644
1363
|
});
|
|
2645
|
-
|
|
2646
|
-
this.store.setList(connection.key, nextListState);
|
|
2647
|
-
return nextListState;
|
|
1364
|
+
return this.store.getListState(connection.key);
|
|
2648
1365
|
}
|
|
2649
1366
|
const owner = parseEntityId(connection.owner);
|
|
2650
1367
|
const requestArgs = omitUndefinedValues({
|
|
@@ -2658,6 +1375,7 @@ var FateClient = class {
|
|
|
2658
1375
|
const parentSelection = /* @__PURE__ */ new Set();
|
|
2659
1376
|
for (const path of nodeSelection) parentSelection.add(`${connection.field}.${path}`);
|
|
2660
1377
|
const [parentRecord] = await this.transport.fetchById(owner.type, [owner.id], parentSelection, scopedArgsPayload);
|
|
1378
|
+
this.assertPersistenceActive();
|
|
2661
1379
|
if (!parentRecord || typeof parentRecord !== "object") return this.store.getListState(connection.key);
|
|
2662
1380
|
const list = parentRecord[connection.field];
|
|
2663
1381
|
const connectionPayload = Array.isArray(list) ? {
|
|
@@ -2668,37 +1386,39 @@ var FateClient = class {
|
|
|
2668
1386
|
pagination: void 0
|
|
2669
1387
|
} : list;
|
|
2670
1388
|
if (!connectionPayload) return this.store.getListState(connection.key);
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
const
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
1389
|
+
this.store.update(() => {
|
|
1390
|
+
const incomingIds = [];
|
|
1391
|
+
const incomingCursors = [];
|
|
1392
|
+
const nodeType = this.getListNodeType(owner.type, connection.field);
|
|
1393
|
+
for (const entry of connectionPayload.items) {
|
|
1394
|
+
const { node } = entry;
|
|
1395
|
+
const id = this.write(nodeType, node, nodeSelection, plan);
|
|
1396
|
+
incomingIds.push(id);
|
|
1397
|
+
incomingCursors.push(entry.cursor);
|
|
1398
|
+
}
|
|
1399
|
+
const previous = this.store.getListState(connection.key);
|
|
1400
|
+
const previousIds = previous?.ids ?? [];
|
|
1401
|
+
const previousSet = new Set(previousIds);
|
|
1402
|
+
const newIds = incomingIds.filter((id) => !previousSet.has(id));
|
|
1403
|
+
const nextListState = this.mergeListState(previous, incomingIds, incomingCursors, connectionPayload.pagination, {
|
|
1404
|
+
...getPaginationMergeInfo(argsPayload),
|
|
1405
|
+
direction,
|
|
1406
|
+
hasCursorArg: true,
|
|
1407
|
+
replace: true
|
|
1408
|
+
});
|
|
1409
|
+
this.store.setList(connection.key, nextListState);
|
|
1410
|
+
const current = this.store.read(connection.owner);
|
|
1411
|
+
const existingField = Array.isArray(current?.[connection.field]) ? current?.[connection.field] || [] : [];
|
|
1412
|
+
const nodeRefs = createNodeRefsForIds(newIds, void 0);
|
|
1413
|
+
const nextField = direction === "forward" ? [...existingField, ...nodeRefs] : [...nodeRefs, ...existingField];
|
|
1414
|
+
this.viewDataCache.invalidate(connection.owner);
|
|
1415
|
+
this.store.merge(connection.owner, { [connection.field]: nextField }, [connection.field]);
|
|
2689
1416
|
});
|
|
2690
|
-
this.store.setList(connection.key, nextListState);
|
|
2691
|
-
const current = this.store.read(connection.owner);
|
|
2692
|
-
const existingField = Array.isArray(current?.[connection.field]) ? current?.[connection.field] || [] : [];
|
|
2693
|
-
const nodeRefs = createNodeRefsForIds(newIds, void 0);
|
|
2694
|
-
const nextField = direction === "forward" ? [...existingField, ...nodeRefs] : [...nodeRefs, ...existingField];
|
|
2695
|
-
this.viewDataCache.invalidate(connection.owner);
|
|
2696
|
-
this.store.merge(connection.owner, { [connection.field]: nextField }, [connection.field]);
|
|
2697
1417
|
return this.store.getListState(connection.key);
|
|
2698
1418
|
}
|
|
2699
1419
|
request(request, options) {
|
|
2700
1420
|
const mode = options?.mode ?? "cache-first";
|
|
2701
|
-
return this.requestWithDescriptor(this.createRequestDescriptor(request), mode, { revalidateExisting: true });
|
|
1421
|
+
return this.requestWithDescriptor(this.createRequestDescriptor(request), mode, { revalidateExisting: true }, options);
|
|
2702
1422
|
}
|
|
2703
1423
|
requestForRender(request, options) {
|
|
2704
1424
|
return this.prepareRequestForRender(request, options).promise;
|
|
@@ -2707,16 +1427,23 @@ var FateClient = class {
|
|
|
2707
1427
|
const mode = options?.mode ?? "cache-first";
|
|
2708
1428
|
const descriptor = this.createRequestDescriptor(request);
|
|
2709
1429
|
return {
|
|
2710
|
-
promise: this.requestWithDescriptor(descriptor, mode, { revalidateExisting: false }),
|
|
1430
|
+
promise: this.requestWithDescriptor(descriptor, mode, { revalidateExisting: false }, options),
|
|
2711
1431
|
requestKey: descriptor.key
|
|
2712
1432
|
};
|
|
2713
1433
|
}
|
|
2714
|
-
requestWithDescriptor(descriptor, mode, { revalidateExisting }) {
|
|
1434
|
+
requestWithDescriptor(descriptor, mode, { revalidateExisting }, options) {
|
|
1435
|
+
this.assertPersistenceActive();
|
|
1436
|
+
if (this.persistence && options?.persist && (!Number.isSafeInteger(options.persist.maxAge) || options.persist.maxAge < 0 || options.persist.maxAge > Number.MAX_SAFE_INTEGER - Date.now())) throw new Error("fate: Persistence maxAge must be a non-negative finite duration.");
|
|
2715
1437
|
const requestKey = descriptor.key;
|
|
2716
1438
|
const existingRequest = this.requests.get(requestKey)?.get(mode);
|
|
2717
1439
|
if (existingRequest) {
|
|
1440
|
+
if (this.persistence && options?.persist) {
|
|
1441
|
+
const executionOptions = this.requestExecutionOptions.get(existingRequest.descriptor);
|
|
1442
|
+
if (executionOptions) executionOptions.persist = { ...options.persist };
|
|
1443
|
+
}
|
|
1444
|
+
if (this.persistence && options?.persist && existingRequest.status === "fulfilled") this.persistence.used(existingRequest.descriptor, options.persist);
|
|
2718
1445
|
const isFulfilledCacheFirstWithMissingData = mode === "cache-first" && existingRequest.status === "fulfilled" && !this.hasRequestData(existingRequest.descriptor);
|
|
2719
|
-
if (existingRequest.status !== "pending" && (isFulfilledCacheFirstWithMissingData || revalidateExisting && (mode === "network-only" || mode === "stale-while-revalidate" || mode === "cache-first" && existingRequest.status === "rejected"))) this.executeRequestHandle(existingRequest, mode);
|
|
1446
|
+
if (existingRequest.status !== "pending" && (isFulfilledCacheFirstWithMissingData || revalidateExisting && (mode === "network-only" || mode === "stale-while-revalidate" || mode === "cache-first" && existingRequest.status === "rejected"))) this.executeRequestHandle(existingRequest, mode, options);
|
|
2720
1447
|
return existingRequest;
|
|
2721
1448
|
}
|
|
2722
1449
|
const handle = new FateRequestPromise(descriptor, () => this.getRequestResultFromDescriptor(descriptor));
|
|
@@ -2726,7 +1453,7 @@ var FateClient = class {
|
|
|
2726
1453
|
this.requests.set(requestKey, requests);
|
|
2727
1454
|
}
|
|
2728
1455
|
requests.set(mode, handle);
|
|
2729
|
-
this.executeRequestHandle(handle, mode);
|
|
1456
|
+
this.executeRequestHandle(handle, mode, options);
|
|
2730
1457
|
return handle;
|
|
2731
1458
|
}
|
|
2732
1459
|
releaseRequest(request, mode) {
|
|
@@ -2771,7 +1498,7 @@ var FateClient = class {
|
|
|
2771
1498
|
* memory-management flows.
|
|
2772
1499
|
*/
|
|
2773
1500
|
gc() {
|
|
2774
|
-
if (this.hasActiveOptimisticUpdates()) {
|
|
1501
|
+
if (this.persistence ? this.store.hasTransientOptimisticUpdates : this.hasActiveOptimisticUpdates()) {
|
|
2775
1502
|
this.gcPending = true;
|
|
2776
1503
|
return;
|
|
2777
1504
|
}
|
|
@@ -2792,6 +1519,10 @@ var FateClient = class {
|
|
|
2792
1519
|
for (const id of listState.ids) markRecord(id);
|
|
2793
1520
|
for (const id of listState.pendingAfterIds ?? []) markRecord(id);
|
|
2794
1521
|
};
|
|
1522
|
+
const optimisticRoots = this.store.getOptimisticRoots();
|
|
1523
|
+
optimisticRoots.baseRecords.forEach((record) => this.markRecordReferences(record, markRecord));
|
|
1524
|
+
optimisticRoots.records.forEach(markRecord);
|
|
1525
|
+
optimisticRoots.lists.forEach(markList);
|
|
2795
1526
|
for (const descriptor of this.operationLifetime.getDescriptors()) for (const item of descriptor.items) {
|
|
2796
1527
|
if (item.kind === "node" || item.kind === "nodes") {
|
|
2797
1528
|
for (const raw of item.ids) markRecord(toEntityId(item.type, raw));
|
|
@@ -2830,17 +1561,18 @@ var FateClient = class {
|
|
|
2830
1561
|
if (keys.size === 0) this.rootLists.delete(type);
|
|
2831
1562
|
}
|
|
2832
1563
|
}
|
|
2833
|
-
executeRequestHandle(handle, mode) {
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
1564
|
+
executeRequestHandle(handle, mode, options) {
|
|
1565
|
+
const executionOptions = {
|
|
1566
|
+
fetchAll: mode !== "cache-first",
|
|
1567
|
+
persist: options?.persist ? { ...options.persist } : void 0
|
|
1568
|
+
};
|
|
1569
|
+
if (this.persistence) this.requestExecutionOptions.set(handle.descriptor, executionOptions);
|
|
1570
|
+
const execute = () => {
|
|
1571
|
+
this.assertPersistenceActive();
|
|
1572
|
+
if (mode === "stale-while-revalidate") return this.handleStoreAndNetworkRequest(handle.descriptor, executionOptions);
|
|
1573
|
+
return this.executeRequest(handle.descriptor, executionOptions).then(() => this.getRequestResultFromDescriptor(handle.descriptor));
|
|
1574
|
+
};
|
|
1575
|
+
handle.start(this.persistence ? this.withPersistenceLifecycle(() => this.persistence.ready.then(() => mode === "network-only" ? void 0 : this.persistence.restoreRequest(handle.descriptor)).then(execute)) : execute());
|
|
2844
1576
|
}
|
|
2845
1577
|
markRecordReferences(record, markRecord) {
|
|
2846
1578
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2861,29 +1593,52 @@ var FateClient = class {
|
|
|
2861
1593
|
walk(record);
|
|
2862
1594
|
}
|
|
2863
1595
|
hasActiveOptimisticUpdates() {
|
|
2864
|
-
return this.
|
|
1596
|
+
return this.store.hasOptimisticUpdates || this.pendingOptimisticMutations.size > 0;
|
|
2865
1597
|
}
|
|
2866
1598
|
hasPendingRequests() {
|
|
2867
1599
|
return this.pendingNetworkRequests > 0;
|
|
2868
1600
|
}
|
|
2869
|
-
|
|
2870
|
-
|
|
1601
|
+
/** @internal */
|
|
1602
|
+
async withPersistenceLifecycle(request) {
|
|
1603
|
+
this.assertPersistenceActive();
|
|
1604
|
+
const signal = this.persistenceDisposal?.signal;
|
|
1605
|
+
let onDispose;
|
|
2871
1606
|
try {
|
|
2872
|
-
|
|
1607
|
+
const execution = request();
|
|
1608
|
+
const result = signal ? await Promise.race([execution, new Promise((_, reject) => {
|
|
1609
|
+
onDispose = () => reject(/* @__PURE__ */ new Error("fate: Persistence was disposed."));
|
|
1610
|
+
signal.addEventListener("abort", onDispose, { once: true });
|
|
1611
|
+
if (signal.aborted) onDispose();
|
|
1612
|
+
})]) : await execution;
|
|
1613
|
+
this.assertPersistenceActive();
|
|
1614
|
+
return result;
|
|
2873
1615
|
} finally {
|
|
2874
|
-
|
|
1616
|
+
if (onDispose) signal?.removeEventListener("abort", onDispose);
|
|
2875
1617
|
}
|
|
2876
1618
|
}
|
|
1619
|
+
async trackPendingRequest(request) {
|
|
1620
|
+
return this.withPersistenceLifecycle(async () => {
|
|
1621
|
+
if (this.persistence) await this.persistence.ready;
|
|
1622
|
+
this.assertPersistenceActive();
|
|
1623
|
+
this.pendingNetworkRequests += 1;
|
|
1624
|
+
try {
|
|
1625
|
+
return await this.withPersistenceLifecycle(request);
|
|
1626
|
+
} finally {
|
|
1627
|
+
this.pendingNetworkRequests -= 1;
|
|
1628
|
+
this.persistence?.changed();
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
2877
1632
|
runPendingGarbageCollection() {
|
|
2878
1633
|
if (this.gcPending && !this.hasActiveOptimisticUpdates()) this.gc();
|
|
2879
1634
|
}
|
|
2880
|
-
async handleStoreAndNetworkRequest(request) {
|
|
1635
|
+
async handleStoreAndNetworkRequest(request, options) {
|
|
2881
1636
|
if (!this.hasRequestData(request)) {
|
|
2882
|
-
await this.executeRequest(request,
|
|
1637
|
+
await this.executeRequest(request, options);
|
|
2883
1638
|
return this.getRequestResultFromDescriptor(request);
|
|
2884
1639
|
}
|
|
2885
1640
|
const result = this.getRequestResultFromDescriptor(request);
|
|
2886
|
-
this.executeRequest(request,
|
|
1641
|
+
this.executeRequest(request, options).catch(() => {});
|
|
2887
1642
|
return result;
|
|
2888
1643
|
}
|
|
2889
1644
|
getRootType(name) {
|
|
@@ -2909,6 +1664,8 @@ var FateClient = class {
|
|
|
2909
1664
|
const fetchAll = options.fetchAll ?? false;
|
|
2910
1665
|
const groups = /* @__PURE__ */ new Map();
|
|
2911
1666
|
const promises = [];
|
|
1667
|
+
const fetchedItems = [];
|
|
1668
|
+
let fetchedAll = true;
|
|
2912
1669
|
for (const item of request.items) if (item.kind === "node" || item.kind === "nodes") {
|
|
2913
1670
|
const fields = item.plan.paths;
|
|
2914
1671
|
const fieldsSignature = [...fields].slice().sort().join(",");
|
|
@@ -2924,23 +1681,46 @@ var FateClient = class {
|
|
|
2924
1681
|
};
|
|
2925
1682
|
groups.set(groupKey, group);
|
|
2926
1683
|
}
|
|
1684
|
+
const fetchedIds = [];
|
|
2927
1685
|
for (const raw of item.ids) {
|
|
2928
1686
|
const entityId = toEntityId(item.type, raw);
|
|
2929
1687
|
const missing = this.store.missingForSelection(entityId, fields);
|
|
2930
|
-
if (fetchAll || missing.size > 0)
|
|
1688
|
+
if (fetchAll || missing.size > 0) {
|
|
1689
|
+
group.ids.push(raw);
|
|
1690
|
+
fetchedIds.push(raw);
|
|
1691
|
+
}
|
|
2931
1692
|
}
|
|
1693
|
+
if (fetchedIds.length) fetchedItems.push({
|
|
1694
|
+
...item,
|
|
1695
|
+
ids: fetchedIds
|
|
1696
|
+
});
|
|
1697
|
+
fetchedAll &&= fetchedIds.length === item.ids.length;
|
|
2932
1698
|
} else {
|
|
2933
1699
|
if (item.kind === "query") {
|
|
2934
1700
|
const hasResult = this.rootRequests.has(item.queryKey);
|
|
2935
1701
|
const entityId = this.rootRequests.get(item.queryKey);
|
|
2936
1702
|
const missing = hasResult && entityId ? this.store.missingForSelection(entityId, item.plan.paths) : item.plan.paths;
|
|
2937
|
-
if (fetchAll || !hasResult || entityId && missing.size > 0)
|
|
1703
|
+
if (fetchAll || !hasResult || entityId && missing.size > 0) {
|
|
1704
|
+
promises.push(this.fetchQueryAndNormalize(item));
|
|
1705
|
+
fetchedItems.push(item);
|
|
1706
|
+
} else fetchedAll = false;
|
|
2938
1707
|
}
|
|
2939
1708
|
if (item.kind !== "list") continue;
|
|
2940
|
-
if (!fetchAll && !getPaginationArgsInfo(item.argsPayload).hasCursorArg && this.hasRootListData(item))
|
|
1709
|
+
if (!fetchAll && !getPaginationArgsInfo(item.argsPayload).hasCursorArg && this.hasRootListData(item)) {
|
|
1710
|
+
fetchedAll = false;
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
2941
1713
|
promises.push(this.fetchListAndNormalize(item));
|
|
1714
|
+
fetchedItems.push(item);
|
|
2942
1715
|
}
|
|
2943
1716
|
await Promise.all([...promises, ...Array.from(groups.values()).map((group) => group.ids.length ? this.fetchByIdAndNormalize(group.type, group.ids, group.fields, group.plan) : Promise.resolve())]);
|
|
1717
|
+
if (this.persistence) {
|
|
1718
|
+
if (fetchedItems.length) this.persistence.fetched(fetchedAll ? request : {
|
|
1719
|
+
items: fetchedItems,
|
|
1720
|
+
key: `partial:${JSON.stringify([request.key, fetchedItems.map((item) => [item.name, "ids" in item ? item.ids : null])])}`
|
|
1721
|
+
}, options.persist);
|
|
1722
|
+
if (!fetchedItems.length || !fetchedAll) this.persistence.used(request, options.persist);
|
|
1723
|
+
}
|
|
2944
1724
|
}
|
|
2945
1725
|
hasRequestData(request) {
|
|
2946
1726
|
for (const item of request.items) {
|
|
@@ -3028,18 +1808,20 @@ var FateClient = class {
|
|
|
3028
1808
|
const resolvedArgs = resolvedArgsFromPlan(plan);
|
|
3029
1809
|
await this.trackPendingRequest(async () => {
|
|
3030
1810
|
const records = await this.transport.fetchById(type, ids, select, resolvedArgs);
|
|
3031
|
-
|
|
1811
|
+
this.assertPersistenceActive();
|
|
1812
|
+
for (const record of records) this.writeEntity(type, record, select, plan, prefix);
|
|
3032
1813
|
});
|
|
3033
1814
|
}
|
|
3034
1815
|
async fetchQueryAndNormalize(item) {
|
|
3035
1816
|
if (!this.transport.fetchQuery) throw new Error(`fate: transport does not support queries. Please add support for 'fetchQuery' in your transport for '${item.name}'.`);
|
|
3036
1817
|
await this.trackPendingRequest(async () => {
|
|
3037
1818
|
const record = await this.transport.fetchQuery(item.name, item.plan.paths, item.argsPayload);
|
|
1819
|
+
this.assertPersistenceActive();
|
|
3038
1820
|
if (!record || typeof record !== "object") {
|
|
3039
1821
|
this.rootRequests.set(item.queryKey, null);
|
|
3040
1822
|
return;
|
|
3041
1823
|
}
|
|
3042
|
-
const entityId = this.writeEntity(item.type, record, item.plan.paths,
|
|
1824
|
+
const entityId = this.writeEntity(item.type, record, item.plan.paths, item.plan);
|
|
3043
1825
|
this.rootRequests.set(item.queryKey, entityId);
|
|
3044
1826
|
});
|
|
3045
1827
|
}
|
|
@@ -3047,133 +1829,132 @@ var FateClient = class {
|
|
|
3047
1829
|
if (!this.transport.fetchList) throw new Error(`fate: 'transport.fetchList' is not configured but request includes a list for key '${item.name}'.`);
|
|
3048
1830
|
await this.trackPendingRequest(async () => {
|
|
3049
1831
|
const { items, pagination } = await this.transport.fetchList(item.name, item.plan.paths, item.argsPayload);
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
const
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
1832
|
+
this.assertPersistenceActive();
|
|
1833
|
+
this.store.update(() => {
|
|
1834
|
+
const ids = [];
|
|
1835
|
+
const cursors = [];
|
|
1836
|
+
for (const entry of items) {
|
|
1837
|
+
const id = this.writeEntity(item.type, entry.node, item.plan.paths, item.plan);
|
|
1838
|
+
ids.push(id);
|
|
1839
|
+
cursors.push(entry.cursor);
|
|
1840
|
+
}
|
|
1841
|
+
if (!filterConnectionArgs(item.argsPayload)) this.registerRootList(item.type, item.listKey);
|
|
1842
|
+
const previous = this.store.getListState(item.listKey);
|
|
1843
|
+
this.store.setList(item.listKey, this.mergeListState(previous, ids, cursors, pagination, {
|
|
1844
|
+
...getPaginationMergeInfo(item.argsPayload),
|
|
1845
|
+
replace: true
|
|
1846
|
+
}));
|
|
1847
|
+
});
|
|
3063
1848
|
});
|
|
3064
1849
|
}
|
|
3065
|
-
writeEntity(type, record, select,
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
const
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
if (
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
if (
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
1850
|
+
writeEntity(type, record, select, plan, pathPrefix = null, insert) {
|
|
1851
|
+
return this.store.update(() => {
|
|
1852
|
+
const config = this.types.get(type);
|
|
1853
|
+
if (!config) throw new Error(`fate: Found unknown entity type '${type}' in normalization.`);
|
|
1854
|
+
const id = config.getId(record);
|
|
1855
|
+
const entityId = toEntityId(type, id);
|
|
1856
|
+
const result = {};
|
|
1857
|
+
const selectionTree = groupSelectionByPrefix(select);
|
|
1858
|
+
if (config.fields) for (const [key, relationDescriptor] of Object.entries(config.fields)) {
|
|
1859
|
+
const value = record[key];
|
|
1860
|
+
const fieldPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
1861
|
+
const fieldArgs = plan?.args.get(fieldPath);
|
|
1862
|
+
if (relationDescriptor === "scalar") {
|
|
1863
|
+
if (!Object.hasOwn(record, key)) continue;
|
|
1864
|
+
result[key] = value;
|
|
1865
|
+
} else if (relationDescriptor && typeof relationDescriptor === "object" && "type" in relationDescriptor) {
|
|
1866
|
+
const childPaths = selectionTree.get(key) ?? emptySet;
|
|
1867
|
+
if (value === null) {
|
|
1868
|
+
result[key] = null;
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
if (value && typeof value === "object" && !isNodeRef(value)) {
|
|
1872
|
+
const childType = relationDescriptor.type;
|
|
1873
|
+
const childConfig = this.types.get(childType);
|
|
1874
|
+
if (!childConfig) throw new Error(`fate: Unknown related type '${childType}' (field '${type}.${key}').`);
|
|
1875
|
+
const childId = toEntityId(childType, childConfig.getId(value));
|
|
1876
|
+
result[key] = createNodeRef(childId);
|
|
1877
|
+
this.writeEntity(childType, value, childPaths, plan, fieldPath);
|
|
1878
|
+
}
|
|
1879
|
+
} else if (relationDescriptor && typeof relationDescriptor === "object" && "listOf" in relationDescriptor) {
|
|
1880
|
+
const childPaths = selectionTree.get(key) ?? emptySet;
|
|
1881
|
+
if (value === null) {
|
|
1882
|
+
result[key] = null;
|
|
1883
|
+
continue;
|
|
1884
|
+
}
|
|
1885
|
+
const childType = relationDescriptor.listOf;
|
|
3088
1886
|
const childConfig = this.types.get(childType);
|
|
3089
1887
|
if (!childConfig) throw new Error(`fate: Unknown related type '${childType}' (field '${type}.${key}').`);
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
if (Array.isArray(record.items)) return {
|
|
3108
|
-
items: record.items.map((node) => {
|
|
3109
|
-
if (node && typeof node === "object" && "node" in node) {
|
|
3110
|
-
const itemRecord = node;
|
|
3111
|
-
return {
|
|
3112
|
-
cursor: itemRecord.cursor,
|
|
3113
|
-
node: itemRecord.node
|
|
3114
|
-
};
|
|
3115
|
-
}
|
|
3116
|
-
return { node };
|
|
3117
|
-
}),
|
|
3118
|
-
pagination: record.pagination
|
|
3119
|
-
};
|
|
3120
|
-
}
|
|
3121
|
-
return null;
|
|
3122
|
-
})();
|
|
3123
|
-
if (connection) {
|
|
3124
|
-
const ids = [];
|
|
3125
|
-
const cursors = [];
|
|
3126
|
-
const nodeSelection = childPaths.size > 0 ? /* @__PURE__ */ new Set() : childPaths;
|
|
3127
|
-
if (childPaths.size > 0) for (const path of childPaths) {
|
|
3128
|
-
if (path.startsWith("items.node.")) {
|
|
3129
|
-
nodeSelection.add(path.slice(11));
|
|
3130
|
-
continue;
|
|
3131
|
-
}
|
|
3132
|
-
if (path.startsWith("node.")) {
|
|
3133
|
-
nodeSelection.add(path.slice(5));
|
|
3134
|
-
continue;
|
|
1888
|
+
const connection = (() => {
|
|
1889
|
+
if (Array.isArray(value)) return { items: value.map((item) => ({ node: item })) };
|
|
1890
|
+
if (value && typeof value === "object") {
|
|
1891
|
+
const record = value;
|
|
1892
|
+
if (Array.isArray(record.items)) return {
|
|
1893
|
+
items: record.items.map((node) => {
|
|
1894
|
+
if (node && typeof node === "object" && "node" in node) {
|
|
1895
|
+
const itemRecord = node;
|
|
1896
|
+
return {
|
|
1897
|
+
cursor: itemRecord.cursor,
|
|
1898
|
+
node: itemRecord.node
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
return { node };
|
|
1902
|
+
}),
|
|
1903
|
+
pagination: record.pagination
|
|
1904
|
+
};
|
|
3135
1905
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
const
|
|
3141
|
-
const
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
1906
|
+
return null;
|
|
1907
|
+
})();
|
|
1908
|
+
if (connection) {
|
|
1909
|
+
const ids = [];
|
|
1910
|
+
const cursors = [];
|
|
1911
|
+
const nodeSelection = childPaths.size > 0 ? /* @__PURE__ */ new Set() : childPaths;
|
|
1912
|
+
if (childPaths.size > 0) for (const path of childPaths) {
|
|
1913
|
+
if (path.startsWith("items.node.")) {
|
|
1914
|
+
nodeSelection.add(path.slice(11));
|
|
1915
|
+
continue;
|
|
1916
|
+
}
|
|
1917
|
+
if (path.startsWith("node.")) {
|
|
1918
|
+
nodeSelection.add(path.slice(5));
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1921
|
+
if (path === "items.node" || path.startsWith("items.")) continue;
|
|
1922
|
+
nodeSelection.add(path);
|
|
3146
1923
|
}
|
|
3147
|
-
|
|
3148
|
-
const
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
1924
|
+
for (const entry of connection.items) {
|
|
1925
|
+
const node = entry.node;
|
|
1926
|
+
const cursor = "cursor" in entry ? entry.cursor : void 0;
|
|
1927
|
+
cursors.push(cursor);
|
|
1928
|
+
if (isNodeRef(node)) {
|
|
1929
|
+
ids.push(getNodeRefId(node));
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
if (node && typeof node === "object") {
|
|
1933
|
+
const childId = toEntityId(childType, childConfig.getId(node));
|
|
1934
|
+
this.writeEntity(childType, node, nodeSelection, plan, fieldPath);
|
|
1935
|
+
ids.push(childId);
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
3152
1938
|
}
|
|
1939
|
+
const listKey = getListKey(entityId, key, fieldArgs?.hash);
|
|
1940
|
+
const previousList = this.store.getListState(listKey);
|
|
1941
|
+
const argsValue = fieldArgs?.value;
|
|
1942
|
+
const nextListState = this.mergeListState(previousList, ids, cursors, connection.pagination, getPaginationMergeInfo(argsValue));
|
|
1943
|
+
const listChanged = !areListStatesEqual(previousList, nextListState);
|
|
1944
|
+
result[key] = createNodeRefsForIds(nextListState.ids, this.store.read(entityId)?.[key], { reuseCurrentArray: !listChanged });
|
|
1945
|
+
this.store.setList(listKey, nextListState);
|
|
3153
1946
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
}
|
|
3164
|
-
for (const [key, value] of Object.entries(record)) if (!(key in (config.fields ?? {}))) {
|
|
3165
|
-
const fieldPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
3166
|
-
if (blockedMask && isCovered(blockedMask, fieldPath)) continue;
|
|
3167
|
-
result[key] = value;
|
|
3168
|
-
}
|
|
3169
|
-
if (snapshots && !snapshots.has(entityId)) snapshots.set(entityId, this.store.snapshot(entityId));
|
|
3170
|
-
this.viewDataCache.invalidate(entityId);
|
|
3171
|
-
this.store.merge(entityId, result, select);
|
|
3172
|
-
this.linkParentLists(type, entityId, result, snapshots, listSnapshots, insert ?? "after");
|
|
3173
|
-
if (!pathPrefix && insert) this.insertIntoRootLists(type, entityId, insert, listSnapshots);
|
|
3174
|
-
return entityId;
|
|
1947
|
+
} else result[key] = value;
|
|
1948
|
+
}
|
|
1949
|
+
for (const [key, value] of Object.entries(record)) if (!(key in (config.fields ?? {}))) result[key] = value;
|
|
1950
|
+
this.viewDataCache.invalidate(entityId);
|
|
1951
|
+
this.store.merge(entityId, result, select);
|
|
1952
|
+
this.linkParentLists(type, entityId, result, insert ?? "after");
|
|
1953
|
+
if (!pathPrefix && insert) this.insertIntoRootLists(type, entityId, insert);
|
|
1954
|
+
return entityId;
|
|
1955
|
+
});
|
|
3175
1956
|
}
|
|
3176
|
-
linkParentLists(type, entityId, record,
|
|
1957
|
+
linkParentLists(type, entityId, record, insert) {
|
|
3177
1958
|
if (insert === "none") return;
|
|
3178
1959
|
const parents = this.parentLists.get(type);
|
|
3179
1960
|
if (!parents) return;
|
|
@@ -3186,23 +1967,18 @@ var FateClient = class {
|
|
|
3186
1967
|
if (!existing) continue;
|
|
3187
1968
|
const current = Array.isArray(existing[parent.field]) ? existing[parent.field] : [];
|
|
3188
1969
|
if (current.some((item) => isNodeRef(item) && getNodeRefId(item) === entityId)) continue;
|
|
3189
|
-
if (snapshots && !snapshots.has(parentId)) snapshots.set(parentId, this.store.snapshot(parentId));
|
|
3190
1970
|
this.viewDataCache.invalidate(parentId);
|
|
3191
1971
|
const defaultListKey = getListKey(parentId, parent.field);
|
|
3192
1972
|
const defaultListState = this.store.getListState(defaultListKey);
|
|
3193
1973
|
let nextField = current;
|
|
3194
1974
|
if (defaultListState) {
|
|
3195
1975
|
const { list: nextDefaultListState, pending } = this.applyListInsert(defaultListState, entityId, insert);
|
|
3196
|
-
if (nextDefaultListState !== defaultListState)
|
|
3197
|
-
this.snapshotList(defaultListKey, listSnapshots);
|
|
3198
|
-
this.store.setList(defaultListKey, nextDefaultListState);
|
|
3199
|
-
}
|
|
1976
|
+
if (nextDefaultListState !== defaultListState) this.store.setList(defaultListKey, nextDefaultListState);
|
|
3200
1977
|
const nextFieldIds = pending ? getListEntries(nextDefaultListState).map(({ id }) => id) : nextDefaultListState.ids;
|
|
3201
1978
|
nextField = createNodeRefsForIds(nextFieldIds, current);
|
|
3202
1979
|
} else {
|
|
3203
1980
|
const currentIds = current.map((item) => isNodeRef(item) ? getNodeRefId(item) : null).filter((id) => id != null);
|
|
3204
1981
|
const nextDefaultListState = { ids: insert === "before" ? [entityId, ...currentIds] : [...currentIds, entityId] };
|
|
3205
|
-
this.snapshotList(defaultListKey, listSnapshots);
|
|
3206
1982
|
this.store.setList(defaultListKey, nextDefaultListState);
|
|
3207
1983
|
nextField = createNodeRefsForIds(nextDefaultListState.ids, current);
|
|
3208
1984
|
}
|
|
@@ -3210,7 +1986,6 @@ var FateClient = class {
|
|
|
3210
1986
|
for (const [listKey, listState] of registeredLists) {
|
|
3211
1987
|
const nextListState = this.applyListInsert(listState, entityId, insert).list;
|
|
3212
1988
|
if (nextListState === listState) continue;
|
|
3213
|
-
this.snapshotList(listKey, listSnapshots);
|
|
3214
1989
|
this.store.setList(listKey, nextListState);
|
|
3215
1990
|
}
|
|
3216
1991
|
this.store.merge(parentId, { [parent.field]: nextField }, [parent.field]);
|
|
@@ -3258,92 +2033,93 @@ var FateClient = class {
|
|
|
3258
2033
|
target[key] = null;
|
|
3259
2034
|
continue;
|
|
3260
2035
|
}
|
|
3261
|
-
if (Array.isArray(value))
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
2036
|
+
if (Array.isArray(value)) {
|
|
2037
|
+
if (nextSelection.items && typeof nextSelection.items === "object") {
|
|
2038
|
+
const selection = nextSelection.items;
|
|
2039
|
+
const fieldArgs = plan.args.get(fieldPath);
|
|
2040
|
+
const listKey = getListKey(parentId, key, fieldArgs?.hash);
|
|
2041
|
+
const listState = this.store.getListState(listKey);
|
|
2042
|
+
const connection = { items: (listState ? getListEntries(listState) : value.map((item) => ({
|
|
2043
|
+
cursor: void 0,
|
|
2044
|
+
id: isNodeRef(item) ? getNodeRefId(item) : null
|
|
2045
|
+
}))).map((entry, index) => {
|
|
2046
|
+
const entityId = entry.id;
|
|
2047
|
+
if (!entityId) return {
|
|
2048
|
+
cursor: listState?.cursors?.[index],
|
|
2049
|
+
node: null
|
|
2050
|
+
};
|
|
2051
|
+
ids.add(entityId);
|
|
2052
|
+
const record = this.store.read(entityId);
|
|
2053
|
+
const { id, type } = parseEntityId(entityId);
|
|
2054
|
+
const node = {
|
|
2055
|
+
__typename: type,
|
|
2056
|
+
id
|
|
2057
|
+
};
|
|
2058
|
+
if (record) walk(selection.node, record, node, entityId, fieldPath);
|
|
2059
|
+
const itemResult = { node: record ? node : null };
|
|
2060
|
+
if (selection.cursor === true) itemResult.cursor = entry.cursor;
|
|
2061
|
+
return itemResult;
|
|
2062
|
+
}) };
|
|
2063
|
+
if ("pagination" in nextSelection && nextSelection.pagination) {
|
|
2064
|
+
const paginationSelection = nextSelection.pagination;
|
|
2065
|
+
const storedPagination = listState?.pagination;
|
|
2066
|
+
if (storedPagination) {
|
|
2067
|
+
const pagination = {};
|
|
2068
|
+
if (paginationSelection.nextCursor === true) {
|
|
2069
|
+
if (storedPagination.nextCursor !== void 0) pagination.nextCursor = storedPagination.nextCursor;
|
|
2070
|
+
}
|
|
2071
|
+
if (paginationSelection.previousCursor === true) {
|
|
2072
|
+
if (storedPagination.previousCursor !== void 0) pagination.previousCursor = storedPagination.previousCursor;
|
|
2073
|
+
}
|
|
2074
|
+
if (paginationSelection.hasNext === true) pagination.hasNext = storedPagination.hasNext;
|
|
2075
|
+
if (paginationSelection.hasPrevious === true) pagination.hasPrevious = storedPagination.hasPrevious;
|
|
2076
|
+
if (Object.keys(pagination).length > 0) connection.pagination = pagination;
|
|
2077
|
+
} else connection.pagination = void 0;
|
|
2078
|
+
}
|
|
2079
|
+
const { id: ownerRawId, type: parentType } = parseEntityId(parentId);
|
|
2080
|
+
if (parentType) {
|
|
2081
|
+
const childType = this.getListNodeType(parentType, key);
|
|
2082
|
+
const metadata = {
|
|
2083
|
+
args: (() => {
|
|
2084
|
+
if (!fieldArgs?.value && ownerRawId === void 0) return;
|
|
2085
|
+
const value = fieldArgs?.value ? { ...fieldArgs.value } : {};
|
|
2086
|
+
if (ownerRawId !== void 0) value.id = ownerRawId;
|
|
2087
|
+
return value;
|
|
2088
|
+
})(),
|
|
2089
|
+
field: key,
|
|
2090
|
+
hash: fieldArgs?.hash,
|
|
2091
|
+
key: listKey,
|
|
2092
|
+
live: plan.live.get(fieldPath),
|
|
2093
|
+
owner: parentId,
|
|
2094
|
+
procedure: `${parentType}.${key}`,
|
|
2095
|
+
root: false,
|
|
2096
|
+
type: childType
|
|
2097
|
+
};
|
|
2098
|
+
Object.defineProperty(connection, ConnectionTag, {
|
|
2099
|
+
configurable: false,
|
|
2100
|
+
enumerable: false,
|
|
2101
|
+
value: metadata,
|
|
2102
|
+
writable: false
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
target[key] = connection;
|
|
2106
|
+
} else target[key] = value.map((item) => {
|
|
2107
|
+
const entityId = isNodeRef(item) ? getNodeRefId(item) : null;
|
|
2108
|
+
if (!entityId) return item;
|
|
3275
2109
|
ids.add(entityId);
|
|
3276
2110
|
const record = this.store.read(entityId);
|
|
3277
2111
|
const { id, type } = parseEntityId(entityId);
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
const storedPagination = listState?.pagination;
|
|
3290
|
-
if (storedPagination) {
|
|
3291
|
-
const pagination = {};
|
|
3292
|
-
if (paginationSelection.nextCursor === true) {
|
|
3293
|
-
if (storedPagination.nextCursor !== void 0) pagination.nextCursor = storedPagination.nextCursor;
|
|
3294
|
-
}
|
|
3295
|
-
if (paginationSelection.previousCursor === true) {
|
|
3296
|
-
if (storedPagination.previousCursor !== void 0) pagination.previousCursor = storedPagination.previousCursor;
|
|
3297
|
-
}
|
|
3298
|
-
if (paginationSelection.hasNext === true) pagination.hasNext = storedPagination.hasNext;
|
|
3299
|
-
if (paginationSelection.hasPrevious === true) pagination.hasPrevious = storedPagination.hasPrevious;
|
|
3300
|
-
if (Object.keys(pagination).length > 0) connection.pagination = pagination;
|
|
3301
|
-
} else connection.pagination = void 0;
|
|
3302
|
-
}
|
|
3303
|
-
const { id: ownerRawId, type: parentType } = parseEntityId(parentId);
|
|
3304
|
-
if (parentType) {
|
|
3305
|
-
const childType = this.getListNodeType(parentType, key);
|
|
3306
|
-
const metadata = {
|
|
3307
|
-
args: (() => {
|
|
3308
|
-
if (!fieldArgs?.value && ownerRawId === void 0) return;
|
|
3309
|
-
const value = fieldArgs?.value ? { ...fieldArgs.value } : {};
|
|
3310
|
-
if (ownerRawId !== void 0) value.id = ownerRawId;
|
|
3311
|
-
return value;
|
|
3312
|
-
})(),
|
|
3313
|
-
field: key,
|
|
3314
|
-
hash: fieldArgs?.hash,
|
|
3315
|
-
key: listKey,
|
|
3316
|
-
live: plan.live.get(fieldPath),
|
|
3317
|
-
owner: parentId,
|
|
3318
|
-
procedure: `${parentType}.${key}`,
|
|
3319
|
-
root: false,
|
|
3320
|
-
type: childType
|
|
3321
|
-
};
|
|
3322
|
-
Object.defineProperty(connection, ConnectionTag, {
|
|
3323
|
-
configurable: false,
|
|
3324
|
-
enumerable: false,
|
|
3325
|
-
value: metadata,
|
|
3326
|
-
writable: false
|
|
3327
|
-
});
|
|
3328
|
-
}
|
|
3329
|
-
target[key] = connection;
|
|
3330
|
-
} else target[key] = value.map((item) => {
|
|
3331
|
-
const entityId = isNodeRef(item) ? getNodeRefId(item) : null;
|
|
3332
|
-
if (!entityId) return item;
|
|
3333
|
-
ids.add(entityId);
|
|
3334
|
-
const record = this.store.read(entityId);
|
|
3335
|
-
const { id, type } = parseEntityId(entityId);
|
|
3336
|
-
if (record) {
|
|
3337
|
-
const node = {
|
|
3338
|
-
__typename: type,
|
|
3339
|
-
id
|
|
3340
|
-
};
|
|
3341
|
-
walk(nextSelection, record, node, entityId, fieldPath);
|
|
3342
|
-
return node;
|
|
3343
|
-
}
|
|
3344
|
-
return null;
|
|
3345
|
-
});
|
|
3346
|
-
else if (isNodeRef(value)) {
|
|
2112
|
+
if (record) {
|
|
2113
|
+
const node = {
|
|
2114
|
+
__typename: type,
|
|
2115
|
+
id
|
|
2116
|
+
};
|
|
2117
|
+
walk(nextSelection, record, node, entityId, fieldPath);
|
|
2118
|
+
return node;
|
|
2119
|
+
}
|
|
2120
|
+
return null;
|
|
2121
|
+
});
|
|
2122
|
+
} else if (isNodeRef(value)) {
|
|
3347
2123
|
const entityId = getNodeRefId(value);
|
|
3348
2124
|
ids.add(entityId);
|
|
3349
2125
|
const relatedRecord = this.store.read(entityId);
|
|
@@ -3440,7 +2216,7 @@ function createClient(options) {
|
|
|
3440
2216
|
* Builds a `Transport` backed by a tRPC client using the configured resolvers
|
|
3441
2217
|
* for by-id queries, lists, and mutations.
|
|
3442
2218
|
*/
|
|
3443
|
-
function createTRPCTransport({ byId, client, lists, mutations, queries }) {
|
|
2219
|
+
function createTRPCTransport({ byId, client, lists, mutateDurably, mutations, queries }) {
|
|
3444
2220
|
const transport = {
|
|
3445
2221
|
async fetchById(type, ids, select, args) {
|
|
3446
2222
|
const resolver = byId[type];
|
|
@@ -3468,7 +2244,8 @@ function createTRPCTransport({ byId, client, lists, mutations, queries }) {
|
|
|
3468
2244
|
args,
|
|
3469
2245
|
select: [...select]
|
|
3470
2246
|
});
|
|
3471
|
-
}
|
|
2247
|
+
},
|
|
2248
|
+
mutateDurably
|
|
3472
2249
|
};
|
|
3473
2250
|
transport.mutate = async (procedure, input, select) => {
|
|
3474
2251
|
const resolver = mutations?.[procedure];
|
|
@@ -3546,7 +2323,7 @@ const responseError = async (response) => {
|
|
|
3546
2323
|
return new FateRequestError(errorCodeFromStatus(response.status), message, { status: response.status });
|
|
3547
2324
|
};
|
|
3548
2325
|
const assertProtocolResponse = (value) => {
|
|
3549
|
-
if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.results) || value.results.some((entry) => !isRecord(entry) || typeof entry.id !== "string")) throw new Error("fate(http): Invalid protocol response.");
|
|
2326
|
+
if (!isRecord(value) || value.version !== 1 && value.version !== 2 || !Array.isArray(value.results) || value.results.some((entry) => !isRecord(entry) || typeof entry.id !== "string")) throw new Error("fate(http): Invalid protocol response.");
|
|
3550
2327
|
return value;
|
|
3551
2328
|
};
|
|
3552
2329
|
const resultValue = (result) => {
|
|
@@ -3584,7 +2361,7 @@ function createHTTPTransport({ eventSource, fetch: fetchImpl = defaultFetch, hea
|
|
|
3584
2361
|
const response = await fetchImpl(endpoint, {
|
|
3585
2362
|
body: JSON.stringify({
|
|
3586
2363
|
operations: batch.map((entry) => entry.operation),
|
|
3587
|
-
version: 1
|
|
2364
|
+
version: batch.some((entry) => entry.operation.mutation) ? 2 : 1
|
|
3588
2365
|
}),
|
|
3589
2366
|
headers: await requestHeaders({ "content-type": "application/json" }, headers),
|
|
3590
2367
|
method: "POST"
|
|
@@ -3641,6 +2418,15 @@ function createHTTPTransport({ eventSource, fetch: fetchImpl = defaultFetch, hea
|
|
|
3641
2418
|
name,
|
|
3642
2419
|
select: [...select]
|
|
3643
2420
|
});
|
|
2421
|
+
},
|
|
2422
|
+
mutateDurably(name, input, select, identity) {
|
|
2423
|
+
return enqueue({
|
|
2424
|
+
input,
|
|
2425
|
+
kind: "mutation",
|
|
2426
|
+
mutation: identity,
|
|
2427
|
+
name,
|
|
2428
|
+
select: [...select]
|
|
2429
|
+
});
|
|
3644
2430
|
}
|
|
3645
2431
|
};
|
|
3646
2432
|
if (liveEnabled) {
|