houdini 1.5.4 → 2.0.0-next.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/build/cmd-cjs/index.js +29194 -22340
- package/build/cmd-esm/index.js +27597 -20744
- package/build/codegen/generators/artifacts/index.d.ts +8 -0
- package/build/codegen/index.d.ts +3 -2
- package/build/codegen/transforms/fragmentVariables.d.ts +1 -1
- package/build/codegen/transforms/list.d.ts +2 -2
- package/build/codegen/transforms/paginate.d.ts +2 -34
- package/build/codegen/utils/flattenSelections.d.ts +1 -1
- package/build/codegen-cjs/index.js +26128 -21041
- package/build/codegen-esm/index.js +24463 -19376
- package/build/lib/fs.d.ts +3 -2
- package/build/lib/index.d.ts +1 -0
- package/build/lib/watchAndRun.d.ts +61 -0
- package/build/lib-cjs/index.js +26668 -20749
- package/build/lib-esm/index.js +25001 -19085
- package/build/runtime-cjs/cache/cache.js +22 -1
- package/build/runtime-cjs/cache/gc.js +1 -0
- package/build/runtime-cjs/cache/index.js +0 -2
- package/build/runtime-cjs/cache/lists.js +9 -0
- package/build/runtime-cjs/cache/staleManager.js +26 -1
- package/build/runtime-cjs/cache/storage.js +4 -0
- package/build/runtime-cjs/cache/subscription.js +1 -0
- package/build/runtime-cjs/client/documentStore.js +28 -2
- package/build/runtime-cjs/client/index.js +17 -1
- package/build/runtime-cjs/client/plugins/cache.js +8 -2
- package/build/runtime-cjs/client/plugins/fetch.js +14 -6
- package/build/runtime-cjs/client/plugins/fetchParams.js +2 -0
- package/build/runtime-cjs/client/plugins/fragment.js +1 -0
- package/build/runtime-cjs/client/plugins/index.js +9 -1
- package/build/runtime-cjs/client/plugins/injectedPlugins.js +0 -2
- package/build/runtime-cjs/client/plugins/mutation.js +6 -0
- package/build/runtime-cjs/client/plugins/optimisticKeys.js +10 -3
- package/build/runtime-cjs/client/plugins/query.js +4 -0
- package/build/runtime-cjs/client/utils/index.js +4 -0
- package/build/runtime-cjs/imports/config.js +0 -2
- package/build/runtime-cjs/imports/pluginConfig.js +0 -2
- package/build/runtime-cjs/index.js +12 -6
- package/build/runtime-cjs/lib/config.js +4 -0
- package/build/runtime-cjs/lib/deepEquals.js +2 -4
- package/build/runtime-cjs/lib/index.js +13 -0
- package/build/runtime-cjs/lib/pagination.js +2 -1
- package/build/runtime-cjs/lib/types.js +11 -1
- package/build/runtime-cjs/public/cache.js +11 -0
- package/build/runtime-cjs/public/record.js +5 -0
- package/build/runtime-cjs/router/jwt.js +15 -28
- package/build/runtime-cjs/router/match.js +2 -4
- package/build/runtime-cjs/router/server.js +6 -1
- package/build/runtime-cjs/router/session.js +1 -1
- package/build/runtime-esm/cache/cache.js +22 -1
- package/build/runtime-esm/cache/gc.js +1 -0
- package/build/runtime-esm/cache/lists.js +9 -0
- package/build/runtime-esm/cache/staleManager.js +26 -1
- package/build/runtime-esm/cache/storage.js +4 -0
- package/build/runtime-esm/cache/subscription.js +1 -0
- package/build/runtime-esm/client/documentStore.js +28 -2
- package/build/runtime-esm/client/index.js +13 -1
- package/build/runtime-esm/client/plugins/cache.js +4 -2
- package/build/runtime-esm/client/plugins/fetch.js +14 -6
- package/build/runtime-esm/client/plugins/fetchParams.js +2 -0
- package/build/runtime-esm/client/plugins/fragment.js +1 -0
- package/build/runtime-esm/client/plugins/mutation.js +6 -0
- package/build/runtime-esm/client/plugins/optimisticKeys.js +6 -3
- package/build/runtime-esm/client/plugins/query.js +4 -0
- package/build/runtime-esm/lib/deepEquals.js +2 -4
- package/build/runtime-esm/lib/pagination.js +2 -1
- package/build/runtime-esm/lib/types.js +9 -0
- package/build/runtime-esm/public/cache.js +11 -0
- package/build/runtime-esm/public/record.js +5 -0
- package/build/runtime-esm/router/jwt.js +15 -28
- package/build/runtime-esm/router/match.js +2 -4
- package/build/runtime-esm/router/server.js +6 -1
- package/build/runtime-esm/router/session.js +1 -1
- package/build/test-cjs/index.js +26112 -21026
- package/build/test-esm/index.js +24447 -19361
- package/build/vite/hmr.d.ts +5 -0
- package/build/vite/imports.d.ts +2 -2
- package/build/vite/schema.d.ts +0 -3
- package/build/vite-cjs/index.js +34670 -31925
- package/build/vite-esm/index.js +31149 -28405
- package/package.json +13 -14
|
@@ -19,21 +19,25 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
};
|
|
20
20
|
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
21
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
26
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
27
|
mod
|
|
24
28
|
));
|
|
25
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
26
|
-
var
|
|
27
|
-
__export(
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
28
32
|
cache: () => cache,
|
|
29
33
|
getCache: () => getCache,
|
|
30
34
|
graphql: () => graphql
|
|
31
35
|
});
|
|
32
|
-
module.exports = __toCommonJS(
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
37
|
var import_cache = __toESM(require("./cache"), 1);
|
|
34
38
|
var import_public = require("./public");
|
|
35
|
-
__reExport(
|
|
36
|
-
__reExport(
|
|
39
|
+
__reExport(index_exports, require("./client"), module.exports);
|
|
40
|
+
__reExport(index_exports, require("./lib"), module.exports);
|
|
37
41
|
function graphql(str) {
|
|
38
42
|
if (globalThis?.process?.env?.HOUDINI_PLUGIN) {
|
|
39
43
|
return str;
|
|
@@ -51,5 +55,7 @@ function getCache() {
|
|
|
51
55
|
0 && (module.exports = {
|
|
52
56
|
cache,
|
|
53
57
|
getCache,
|
|
54
|
-
graphql
|
|
58
|
+
graphql,
|
|
59
|
+
...require("./client"),
|
|
60
|
+
...require("./lib")
|
|
55
61
|
});
|
|
@@ -18,6 +18,10 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
20
20
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
25
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
26
|
mod
|
|
23
27
|
));
|
|
@@ -22,8 +22,7 @@ __export(deepEquals_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(deepEquals_exports);
|
|
24
24
|
function deepEquals(objA, objB, map = /* @__PURE__ */ new WeakMap()) {
|
|
25
|
-
if (Object.is(objA, objB))
|
|
26
|
-
return true;
|
|
25
|
+
if (Object.is(objA, objB)) return true;
|
|
27
26
|
if (objA instanceof Date && objB instanceof Date) {
|
|
28
27
|
return objA.getTime() === objB.getTime();
|
|
29
28
|
}
|
|
@@ -33,8 +32,7 @@ function deepEquals(objA, objB, map = /* @__PURE__ */ new WeakMap()) {
|
|
|
33
32
|
if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) {
|
|
34
33
|
return false;
|
|
35
34
|
}
|
|
36
|
-
if (map.get(objA) === objB)
|
|
37
|
-
return true;
|
|
35
|
+
if (map.get(objA) === objB) return true;
|
|
38
36
|
map.set(objA, objB);
|
|
39
37
|
const keysA = Reflect.ownKeys(objA);
|
|
40
38
|
const keysB = Reflect.ownKeys(objB);
|
|
@@ -25,3 +25,16 @@ __reExport(lib_exports, require("./store"), module.exports);
|
|
|
25
25
|
__reExport(lib_exports, require("./key"), module.exports);
|
|
26
26
|
__reExport(lib_exports, require("./lru"), module.exports);
|
|
27
27
|
__reExport(lib_exports, require("./selection"), module.exports);
|
|
28
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
29
|
+
0 && (module.exports = {
|
|
30
|
+
...require("./config"),
|
|
31
|
+
...require("./constants"),
|
|
32
|
+
...require("./deepEquals"),
|
|
33
|
+
...require("./log"),
|
|
34
|
+
...require("./scalars"),
|
|
35
|
+
...require("./types"),
|
|
36
|
+
...require("./store"),
|
|
37
|
+
...require("./key"),
|
|
38
|
+
...require("./lru"),
|
|
39
|
+
...require("./selection")
|
|
40
|
+
});
|
|
@@ -143,7 +143,8 @@ function cursorHandlers({
|
|
|
143
143
|
const queryVariables = {};
|
|
144
144
|
const count = (0, import_pageInfo.countPage)(artifact.refetch.path.concat("edges"), getState()) || artifact.refetch.pageSize;
|
|
145
145
|
if (count && count > artifact.refetch.pageSize) {
|
|
146
|
-
if (currentPageInfo.hasPreviousPage && currentPageInfo.hasNextPage &&
|
|
146
|
+
if (currentPageInfo.hasPreviousPage && currentPageInfo.hasNextPage && // only log if they haven't provided special parameters
|
|
147
|
+
!(variables?.["first"] && variables?.["after"] || variables?.["last"] && variables?.["before"])) {
|
|
147
148
|
console.warn(`\u26A0\uFE0F Encountered a fetch() in the middle of the connection.
|
|
148
149
|
Make sure to pass a cursor value by hand that includes the current set (ie the entry before startCursor)
|
|
149
150
|
`);
|
|
@@ -67,8 +67,17 @@ const RefetchUpdateMode = {
|
|
|
67
67
|
replace: "replace"
|
|
68
68
|
};
|
|
69
69
|
const DataSource = {
|
|
70
|
+
/**
|
|
71
|
+
* from the browser cache
|
|
72
|
+
*/
|
|
70
73
|
Cache: "cache",
|
|
74
|
+
/**
|
|
75
|
+
* from a browser side `fetch`
|
|
76
|
+
*/
|
|
71
77
|
Network: "network",
|
|
78
|
+
/**
|
|
79
|
+
* from a server side `fetch`
|
|
80
|
+
*/
|
|
72
81
|
Ssr: "ssr"
|
|
73
82
|
};
|
|
74
83
|
const fragmentKey = " $fragments";
|
|
@@ -90,5 +99,6 @@ function isPending(value) {
|
|
|
90
99
|
PendingValue,
|
|
91
100
|
RefetchUpdateMode,
|
|
92
101
|
fragmentKey,
|
|
93
|
-
isPending
|
|
102
|
+
isPending,
|
|
103
|
+
...require("../router/types")
|
|
94
104
|
});
|
|
@@ -29,6 +29,9 @@ class Cache {
|
|
|
29
29
|
constructor(cache) {
|
|
30
30
|
this._internal_unstable = cache;
|
|
31
31
|
}
|
|
32
|
+
// if the user is using the imperative API, we want the ability to break the API
|
|
33
|
+
// with any minor version. In order to do this, we require them to accept this contract
|
|
34
|
+
// through their config file
|
|
32
35
|
validateInstabilityWarning() {
|
|
33
36
|
if (!this.config.acceptImperativeInstability && !this.config.features?.imperativeCache) {
|
|
34
37
|
console.warn(`\u26A0\uFE0F The imperative cache API is considered unstable and will change in any minor version release
|
|
@@ -36,6 +39,7 @@ Please acknowledge this by enabling the imperative cache feature flage in your c
|
|
|
36
39
|
For more information: https://houdinigraphql.com/api/cache`);
|
|
37
40
|
}
|
|
38
41
|
}
|
|
42
|
+
// return the record proxy for the given type/id combo
|
|
39
43
|
get(type, data) {
|
|
40
44
|
this.validateInstabilityWarning();
|
|
41
45
|
let recordID = this._internal_unstable._internal_unstable.id(type, data);
|
|
@@ -79,6 +83,7 @@ For more information: https://houdinigraphql.com/api/cache`);
|
|
|
79
83
|
this.validateInstabilityWarning();
|
|
80
84
|
this._internal_unstable.write({
|
|
81
85
|
selection: query.artifact.selection,
|
|
86
|
+
// @ts-expect-error
|
|
82
87
|
data,
|
|
83
88
|
variables: (0, import_lib.marshalInputs)({
|
|
84
89
|
config: this.config,
|
|
@@ -88,9 +93,15 @@ For more information: https://houdinigraphql.com/api/cache`);
|
|
|
88
93
|
});
|
|
89
94
|
return;
|
|
90
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Mark some elements of the cache stale.
|
|
98
|
+
*/
|
|
91
99
|
markStale(type, options) {
|
|
92
100
|
return this._internal_unstable.markTypeStale(type ? { ...options, type } : void 0);
|
|
93
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Reset the entire cache by clearing all records and lists
|
|
104
|
+
*/
|
|
94
105
|
reset() {
|
|
95
106
|
return this._internal_unstable.reset();
|
|
96
107
|
}
|
|
@@ -25,6 +25,7 @@ __export(jwt_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(jwt_exports);
|
|
26
26
|
function base64UrlParse(s) {
|
|
27
27
|
return new Uint8Array(
|
|
28
|
+
// @ts-ignore
|
|
28
29
|
Array.prototype.map.call(
|
|
29
30
|
atob(s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "")),
|
|
30
31
|
(c) => c.charCodeAt(0)
|
|
@@ -77,20 +78,16 @@ function _decodePayload(raw) {
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
async function encode(payload, secret, options = { algorithm: "HS256", header: { typ: "JWT" } }) {
|
|
80
|
-
if (typeof options === "string")
|
|
81
|
-
options = { algorithm: options, header: { typ: "JWT" } };
|
|
81
|
+
if (typeof options === "string") options = { algorithm: options, header: { typ: "JWT" } };
|
|
82
82
|
options = { algorithm: "HS256", header: { typ: "JWT" }, ...options };
|
|
83
83
|
if (payload === null || typeof payload !== "object")
|
|
84
84
|
throw new Error("payload must be an object");
|
|
85
85
|
if (typeof secret !== "string" && typeof secret !== "object")
|
|
86
86
|
throw new Error("secret must be a string or a JWK object");
|
|
87
|
-
if (typeof options.algorithm !== "string")
|
|
88
|
-
throw new Error("options.algorithm must be a string");
|
|
87
|
+
if (typeof options.algorithm !== "string") throw new Error("options.algorithm must be a string");
|
|
89
88
|
const algorithm = algorithms[options.algorithm];
|
|
90
|
-
if (!algorithm)
|
|
91
|
-
|
|
92
|
-
if (!payload.iat)
|
|
93
|
-
payload.iat = Math.floor(Date.now() / 1e3);
|
|
89
|
+
if (!algorithm) throw new Error("algorithm not found");
|
|
90
|
+
if (!payload.iat) payload.iat = Math.floor(Date.now() / 1e3);
|
|
94
91
|
const payloadAsJSON = JSON.stringify(payload);
|
|
95
92
|
const partialToken = `${base64UrlStringify(
|
|
96
93
|
_utf8ToUint8Array(JSON.stringify({ ...options.header, alg: options.algorithm }))
|
|
@@ -105,42 +102,33 @@ async function encode(payload, secret, options = { algorithm: "HS256", header: {
|
|
|
105
102
|
keyData = _str2ab(
|
|
106
103
|
secret.replace(/-----BEGIN.*?-----/g, "").replace(/-----END.*?-----/g, "").replace(/\s/g, "")
|
|
107
104
|
);
|
|
108
|
-
} else
|
|
109
|
-
keyData = _utf8ToUint8Array(secret);
|
|
105
|
+
} else keyData = _utf8ToUint8Array(secret);
|
|
110
106
|
const key = await crypto.subtle.importKey(keyFormat, keyData, algorithm, false, ["sign"]);
|
|
111
107
|
const signature = await crypto.subtle.sign(algorithm, key, _utf8ToUint8Array(partialToken));
|
|
112
108
|
return `${partialToken}.${base64UrlStringify(new Uint8Array(signature))}`;
|
|
113
109
|
}
|
|
114
110
|
async function verify(token, secret, options = { algorithm: "HS256", throwError: false }) {
|
|
115
|
-
if (typeof options === "string")
|
|
116
|
-
options = { algorithm: options, throwError: false };
|
|
111
|
+
if (typeof options === "string") options = { algorithm: options, throwError: false };
|
|
117
112
|
options = { algorithm: "HS256", throwError: false, ...options };
|
|
118
|
-
if (typeof token !== "string")
|
|
119
|
-
throw new Error("token must be a string");
|
|
113
|
+
if (typeof token !== "string") throw new Error("token must be a string");
|
|
120
114
|
if (typeof secret !== "string" && typeof secret !== "object")
|
|
121
115
|
throw new Error("secret must be a string or a JWK object");
|
|
122
|
-
if (typeof options.algorithm !== "string")
|
|
123
|
-
throw new Error("options.algorithm must be a string");
|
|
116
|
+
if (typeof options.algorithm !== "string") throw new Error("options.algorithm must be a string");
|
|
124
117
|
const tokenParts = token.split(".");
|
|
125
|
-
if (tokenParts.length !== 3)
|
|
126
|
-
throw new Error("token must consist of 3 parts");
|
|
118
|
+
if (tokenParts.length !== 3) throw new Error("token must consist of 3 parts");
|
|
127
119
|
const algorithm = algorithms[options.algorithm];
|
|
128
|
-
if (!algorithm)
|
|
129
|
-
throw new Error("algorithm not found");
|
|
120
|
+
if (!algorithm) throw new Error("algorithm not found");
|
|
130
121
|
const { payload } = decode(token);
|
|
131
122
|
if (!payload) {
|
|
132
|
-
if (options.throwError)
|
|
133
|
-
throw "PARSE_ERROR";
|
|
123
|
+
if (options.throwError) throw "PARSE_ERROR";
|
|
134
124
|
return false;
|
|
135
125
|
}
|
|
136
126
|
if (payload.nbf && payload.nbf > Math.floor(Date.now() / 1e3)) {
|
|
137
|
-
if (options.throwError)
|
|
138
|
-
throw "NOT_YET_VALID";
|
|
127
|
+
if (options.throwError) throw "NOT_YET_VALID";
|
|
139
128
|
return false;
|
|
140
129
|
}
|
|
141
130
|
if (payload.exp && payload.exp <= Math.floor(Date.now() / 1e3)) {
|
|
142
|
-
if (options.throwError)
|
|
143
|
-
throw "EXPIRED";
|
|
131
|
+
if (options.throwError) throw "EXPIRED";
|
|
144
132
|
return false;
|
|
145
133
|
}
|
|
146
134
|
let keyFormat = "raw";
|
|
@@ -153,8 +141,7 @@ async function verify(token, secret, options = { algorithm: "HS256", throwError:
|
|
|
153
141
|
keyData = _str2ab(
|
|
154
142
|
secret.replace(/-----BEGIN.*?-----/g, "").replace(/-----END.*?-----/g, "").replace(/\s/g, "")
|
|
155
143
|
);
|
|
156
|
-
} else
|
|
157
|
-
keyData = _utf8ToUint8Array(secret);
|
|
144
|
+
} else keyData = _utf8ToUint8Array(secret);
|
|
158
145
|
const key = await crypto.subtle.importKey(keyFormat, keyData, algorithm, false, ["verify"]);
|
|
159
146
|
return await crypto.subtle.verify(
|
|
160
147
|
algorithm,
|
|
@@ -138,14 +138,12 @@ function exec(match, params) {
|
|
|
138
138
|
}
|
|
139
139
|
buffered = "";
|
|
140
140
|
if (value === void 0) {
|
|
141
|
-
if (param.rest)
|
|
142
|
-
result[param.name] = "";
|
|
141
|
+
if (param.rest) result[param.name] = "";
|
|
143
142
|
} else {
|
|
144
143
|
result[param.name] = decodeURIComponent(value);
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
|
-
if (buffered)
|
|
148
|
-
return;
|
|
146
|
+
if (buffered) return;
|
|
149
147
|
return result;
|
|
150
148
|
}
|
|
151
149
|
function escape(str) {
|
|
@@ -51,7 +51,12 @@ function _serverHandler({
|
|
|
51
51
|
if (schema) {
|
|
52
52
|
client.registerProxy(graphqlEndpoint, async ({ query, variables, session }) => {
|
|
53
53
|
const parsed = (0, import_graphql.parse)(query);
|
|
54
|
-
return await (0, import_graphql.execute)(
|
|
54
|
+
return await (0, import_graphql.execute)({
|
|
55
|
+
schema,
|
|
56
|
+
document: parsed,
|
|
57
|
+
contextValue: session,
|
|
58
|
+
variableValues: variables
|
|
59
|
+
});
|
|
55
60
|
});
|
|
56
61
|
}
|
|
57
62
|
return async (request) => {
|
|
@@ -59,7 +59,7 @@ async function redirect_auth(args) {
|
|
|
59
59
|
}
|
|
60
60
|
const session_cookie_name = "__houdini__";
|
|
61
61
|
async function set_session(req, response, value) {
|
|
62
|
-
const today = new Date();
|
|
62
|
+
const today = /* @__PURE__ */ new Date();
|
|
63
63
|
const expires = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1e3);
|
|
64
64
|
const serialized = await (0, import_jwt.encode)(value, req.session_keys[0]);
|
|
65
65
|
response.headers.set(
|
|
@@ -11,6 +11,9 @@ import { InMemoryStorage } from "./storage";
|
|
|
11
11
|
import { evaluateKey, rootID } from "./stuff";
|
|
12
12
|
import { InMemorySubscriptions } from "./subscription";
|
|
13
13
|
class Cache {
|
|
14
|
+
// the internal implementation for a lot of the cache's methods are moved into
|
|
15
|
+
// a second class to avoid users from relying on unstable APIs. typescript's private
|
|
16
|
+
// label accomplishes this but would not prevent someone using vanilla js
|
|
14
17
|
_internal_unstable;
|
|
15
18
|
constructor({
|
|
16
19
|
disabled,
|
|
@@ -33,6 +36,8 @@ class Cache {
|
|
|
33
36
|
this.setConfig(defaultConfigValues(config));
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
// walk down the selection and save the values that we encounter.
|
|
40
|
+
// any changes will notify subscribers. writing to an optimistic layer will resolve it
|
|
36
41
|
write({
|
|
37
42
|
layer: layerID,
|
|
38
43
|
notifySubscribers = [],
|
|
@@ -43,6 +48,7 @@ class Cache {
|
|
|
43
48
|
this.#notifySubscribers(subscribers.concat(notifySubscribers));
|
|
44
49
|
return subscribers;
|
|
45
50
|
}
|
|
51
|
+
// reconstruct an object with the fields/relations specified by a selection
|
|
46
52
|
read(...args) {
|
|
47
53
|
const { data, partial, stale, hasData } = this._internal_unstable.getSelection(...args);
|
|
48
54
|
if (!hasData) {
|
|
@@ -54,6 +60,7 @@ class Cache {
|
|
|
54
60
|
stale
|
|
55
61
|
};
|
|
56
62
|
}
|
|
63
|
+
// register the provided callbacks with the fields specified by the selection
|
|
57
64
|
subscribe(spec, variables = {}) {
|
|
58
65
|
if (this._internal_unstable.disabled) {
|
|
59
66
|
return;
|
|
@@ -65,6 +72,7 @@ class Cache {
|
|
|
65
72
|
variables
|
|
66
73
|
});
|
|
67
74
|
}
|
|
75
|
+
// stop listening to a particular subscription
|
|
68
76
|
unsubscribe(spec, variables = {}) {
|
|
69
77
|
return this._internal_unstable.subscriptions.remove(
|
|
70
78
|
spec.parentID || rootID,
|
|
@@ -73,6 +81,7 @@ class Cache {
|
|
|
73
81
|
variables
|
|
74
82
|
);
|
|
75
83
|
}
|
|
84
|
+
// return the list handler to mutate a named list in the cache
|
|
76
85
|
list(name, parentID, allLists, skipMatches) {
|
|
77
86
|
const handler = this._internal_unstable.lists.get(name, parentID, allLists, skipMatches);
|
|
78
87
|
if (!handler) {
|
|
@@ -82,10 +91,12 @@ class Cache {
|
|
|
82
91
|
}
|
|
83
92
|
return handler;
|
|
84
93
|
}
|
|
94
|
+
// when an optimistic key resolves, we might momentarily know the same record by different IDs
|
|
85
95
|
registerKeyMap(source, mapped) {
|
|
86
96
|
this._internal_unstable.storage.registerIDMapping(source, mapped);
|
|
87
97
|
this._internal_unstable.subscriptions.copySubscribers(source, mapped);
|
|
88
98
|
}
|
|
99
|
+
// remove the record from the cache's store and unsubscribe from it
|
|
89
100
|
delete(id, layer) {
|
|
90
101
|
const recordIDs = [this._internal_unstable.storage.idMaps[id], id].filter(
|
|
91
102
|
Boolean
|
|
@@ -96,6 +107,7 @@ class Cache {
|
|
|
96
107
|
this._internal_unstable.storage.delete(recordID, layer);
|
|
97
108
|
}
|
|
98
109
|
}
|
|
110
|
+
// set the cache's config
|
|
99
111
|
setConfig(config) {
|
|
100
112
|
this._internal_unstable.setConfig(config);
|
|
101
113
|
}
|
|
@@ -177,6 +189,7 @@ class Cache {
|
|
|
177
189
|
}
|
|
178
190
|
this.#notifySubscribers(toNotify);
|
|
179
191
|
}
|
|
192
|
+
// reset the whole cache
|
|
180
193
|
reset() {
|
|
181
194
|
const subSpecs = this._internal_unstable.subscriptions.reset();
|
|
182
195
|
this._internal_unstable.staleManager.reset();
|
|
@@ -206,6 +219,7 @@ class Cache {
|
|
|
206
219
|
}
|
|
207
220
|
}
|
|
208
221
|
class CacheInternal {
|
|
222
|
+
// for server-side requests we need to be able to flag the cache as disabled so we dont write to it
|
|
209
223
|
disabled = false;
|
|
210
224
|
_config;
|
|
211
225
|
storage;
|
|
@@ -370,7 +384,8 @@ class CacheInternal {
|
|
|
370
384
|
forceNotify
|
|
371
385
|
});
|
|
372
386
|
}
|
|
373
|
-
} else if (Array.isArray(value) &&
|
|
387
|
+
} else if (Array.isArray(value) && // make typescript happy
|
|
388
|
+
(typeof previousValue === "undefined" || previousValue === null || Array.isArray(previousValue))) {
|
|
374
389
|
let oldIDs = [...previousValue || []];
|
|
375
390
|
const emptyEdges = !updates ? [] : oldIDs.map((id) => {
|
|
376
391
|
if (!id) {
|
|
@@ -566,6 +581,7 @@ class CacheInternal {
|
|
|
566
581
|
}
|
|
567
582
|
return toNotify;
|
|
568
583
|
}
|
|
584
|
+
// reconstruct an object defined by its selection
|
|
569
585
|
getSelection({
|
|
570
586
|
selection,
|
|
571
587
|
parent = rootID,
|
|
@@ -754,6 +770,8 @@ class CacheInternal {
|
|
|
754
770
|
}
|
|
755
771
|
return {
|
|
756
772
|
data: cascadeNull ? null : target,
|
|
773
|
+
// our value is considered true if there is some data but not everything
|
|
774
|
+
// has a full value
|
|
757
775
|
partial: !generateLoading && hasData && partial,
|
|
758
776
|
stale: hasData && stale,
|
|
759
777
|
hasData
|
|
@@ -769,12 +787,15 @@ class CacheInternal {
|
|
|
769
787
|
}
|
|
770
788
|
return type + ":" + id;
|
|
771
789
|
}
|
|
790
|
+
// the list of fields that we need in order to compute an objects id
|
|
772
791
|
idFields(type) {
|
|
773
792
|
return keyFieldsForType(this.config, type);
|
|
774
793
|
}
|
|
775
794
|
computeID(type, data) {
|
|
776
795
|
return computeID(this.config, type, data);
|
|
777
796
|
}
|
|
797
|
+
// figure out if this is an embedded object or a linked one by looking for all of the fields marked as
|
|
798
|
+
// required to compute the entity's id
|
|
778
799
|
isEmbedded(linkedType, value) {
|
|
779
800
|
const idFields = this.idFields(linkedType);
|
|
780
801
|
return idFields.length === 0 || idFields.filter((field) => typeof value[field] === "undefined").length > 0;
|
|
@@ -7,6 +7,7 @@ class ListManager {
|
|
|
7
7
|
this.rootID = rootID2;
|
|
8
8
|
this.cache = cache;
|
|
9
9
|
}
|
|
10
|
+
// associate list names with the handler that wraps the list
|
|
10
11
|
lists = /* @__PURE__ */ new Map();
|
|
11
12
|
listsByField = /* @__PURE__ */ new Map();
|
|
12
13
|
get(listName, id, allLists, skipMatches) {
|
|
@@ -145,6 +146,8 @@ class List {
|
|
|
145
146
|
get fieldRef() {
|
|
146
147
|
return `${this.recordID}.${this.key}`;
|
|
147
148
|
}
|
|
149
|
+
// looks for the collection of all of the lists in the cache that satisfies a when
|
|
150
|
+
// condition
|
|
148
151
|
when(when) {
|
|
149
152
|
return this.manager.lists.get(this.name).get(this.recordID).when(when);
|
|
150
153
|
}
|
|
@@ -301,6 +304,8 @@ class List {
|
|
|
301
304
|
const subscribers = this.cache._internal_unstable.subscriptions.get(this.recordID, this.key);
|
|
302
305
|
this.cache._internal_unstable.subscriptions.remove(
|
|
303
306
|
targetID,
|
|
307
|
+
// if we are unsubscribing from a connection, the fields we care about
|
|
308
|
+
// are tucked away under edges
|
|
304
309
|
this.connection ? this.selection.fields.edges.selection : this.selection,
|
|
305
310
|
subscribers.map((sub) => sub[0]),
|
|
306
311
|
variables
|
|
@@ -359,6 +364,8 @@ class List {
|
|
|
359
364
|
this.addToList(selection, data, variables, where, layer);
|
|
360
365
|
}
|
|
361
366
|
}
|
|
367
|
+
// iterating over the list handler should be the same as iterating over
|
|
368
|
+
// the underlying linked list
|
|
362
369
|
*[Symbol.iterator]() {
|
|
363
370
|
let entries = [];
|
|
364
371
|
let value = this.cache._internal_unstable.storage.get(this.recordID, this.key).value;
|
|
@@ -417,6 +424,8 @@ class ListCollection {
|
|
|
417
424
|
deleteListWithKey(key) {
|
|
418
425
|
return this.lists = this.lists.filter((list) => list.key !== key);
|
|
419
426
|
}
|
|
427
|
+
// iterating over the collection should be the same as iterating over
|
|
428
|
+
// the underlying list
|
|
420
429
|
*[Symbol.iterator]() {
|
|
421
430
|
for (let list of this.lists) {
|
|
422
431
|
for (const entry of list) {
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { computeKey } from "../lib";
|
|
2
2
|
class StaleManager {
|
|
3
3
|
cache;
|
|
4
|
+
// id { "User:1" "_ROOT_"
|
|
5
|
+
// field { "id" "viewer"
|
|
6
|
+
// number | undefined | null
|
|
7
|
+
// }
|
|
8
|
+
// }
|
|
9
|
+
// number => data ok (not stale!)
|
|
10
|
+
// undefined => no data (not stale!)
|
|
11
|
+
// null => data stale (stale)
|
|
12
|
+
// nulls mean that the value is stale, and the number is the time that the value was set
|
|
4
13
|
fieldsTime = /* @__PURE__ */ new Map();
|
|
5
14
|
constructor(cache) {
|
|
6
15
|
this.cache = cache;
|
|
@@ -10,13 +19,28 @@ class StaleManager {
|
|
|
10
19
|
this.fieldsTime.set(id, /* @__PURE__ */ new Map());
|
|
11
20
|
}
|
|
12
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* get the FieldTime info
|
|
24
|
+
* @param id User:1
|
|
25
|
+
* @param field firstName
|
|
26
|
+
*/
|
|
13
27
|
getFieldTime(id, field) {
|
|
14
28
|
return this.fieldsTime.get(id)?.get(field);
|
|
15
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* set the date to a field
|
|
32
|
+
* @param id User:1
|
|
33
|
+
* @param field firstName
|
|
34
|
+
*/
|
|
16
35
|
setFieldTimeToNow(id, field) {
|
|
17
36
|
this.#initMapId(id);
|
|
18
|
-
this.fieldsTime.get(id)?.set(field, new Date().valueOf());
|
|
37
|
+
this.fieldsTime.get(id)?.set(field, (/* @__PURE__ */ new Date()).valueOf());
|
|
19
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* set null to a field (stale)
|
|
41
|
+
* @param id User:1
|
|
42
|
+
* @param field firstName
|
|
43
|
+
*/
|
|
20
44
|
markFieldStale(id, field) {
|
|
21
45
|
this.#initMapId(id);
|
|
22
46
|
this.fieldsTime.get(id)?.set(field, null);
|
|
@@ -57,6 +81,7 @@ class StaleManager {
|
|
|
57
81
|
}
|
|
58
82
|
}
|
|
59
83
|
}
|
|
84
|
+
// clean up the stale manager
|
|
60
85
|
delete(id, field) {
|
|
61
86
|
if (this.fieldsTime.has(id)) {
|
|
62
87
|
this.fieldsTime.get(id)?.delete(field);
|
|
@@ -17,6 +17,7 @@ class InMemoryStorage {
|
|
|
17
17
|
this.idMaps[from] = to;
|
|
18
18
|
this.idMaps[to] = from;
|
|
19
19
|
}
|
|
20
|
+
// create a layer and return its id
|
|
20
21
|
createLayer(optimistic = false) {
|
|
21
22
|
const layer = new Layer(this.idCount++);
|
|
22
23
|
layer.optimistic = optimistic;
|
|
@@ -184,6 +185,8 @@ class InMemoryStorage {
|
|
|
184
185
|
}
|
|
185
186
|
return this.data[this.data.length - 1];
|
|
186
187
|
}
|
|
188
|
+
// return a string representation of all of the data and necessary state to
|
|
189
|
+
// recreate the information stored
|
|
187
190
|
serialize() {
|
|
188
191
|
return JSON.stringify({
|
|
189
192
|
rank: this.rank,
|
|
@@ -317,6 +320,7 @@ class Layer {
|
|
|
317
320
|
[id]: {
|
|
318
321
|
...this.operations[id],
|
|
319
322
|
deleted: true,
|
|
323
|
+
// reapply any delete undos
|
|
320
324
|
undoDeletesInList: []
|
|
321
325
|
}
|
|
322
326
|
};
|