houdini 0.13.0-alpha.3 → 0.13.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 +2 -0
- package/build/cmd.js +0 -0
- package/build/runtime/cache/cache.d.ts +2 -1
- package/build/runtime/cache/cache.js +4 -19
- package/build/runtime/index.d.ts +1 -1
- package/build/runtime/index.js +1 -0
- package/build/runtime/network.js +0 -1
- package/build/runtime-cjs/cache/cache.d.ts +2 -1
- package/build/runtime-cjs/cache/cache.js +4 -19
- package/build/runtime-cjs/index.d.ts +1 -1
- package/build/runtime-cjs/index.js +1 -0
- package/build/runtime-cjs/network.js +0 -1
- package/build/runtime-esm/cache/cache.d.ts +2 -1
- package/build/runtime-esm/cache/cache.js +4 -19
- package/build/runtime-esm/index.d.ts +1 -1
- package/build/runtime-esm/index.js +1 -1
- package/build/runtime-esm/network.js +0 -1
- package/package.json +3 -3
- package/runtime/cache/cache.ts +5 -21
- package/runtime/cache/tests/list.test.ts +1 -2
- package/runtime/index.ts +1 -1
- package/runtime/network.ts +0 -1
- package/build/runtime-esm/cache/list.d.ts +0 -35
- package/build/runtime-esm/cache/list.js +0 -203
- package/build/runtime-esm/cache/record.d.ts +0 -40
- package/build/runtime-esm/cache/record.js +0 -195
package/README.md
CHANGED
|
@@ -208,6 +208,8 @@ If you have updated your schema on the server, you can pull down the most recent
|
|
|
208
208
|
npx houdini generate --pull-schema
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
+
I know this sounds like a huge burden but we have plans for removing this step from the developer experience, it's just not ready yet.
|
|
212
|
+
|
|
211
213
|
## 📄 Config File
|
|
212
214
|
|
|
213
215
|
All configuration for your houdini application is defined in a single file that is imported by both the runtime and the
|
package/build/cmd.js
CHANGED
|
File without changes
|
|
@@ -71,7 +71,7 @@ declare class CacheInternal {
|
|
|
71
71
|
variables?: {};
|
|
72
72
|
linkedList: LinkedList;
|
|
73
73
|
}): LinkedList<GraphQLValue>;
|
|
74
|
-
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, }: {
|
|
74
|
+
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, startingWith, }: {
|
|
75
75
|
value: GraphQLValue[];
|
|
76
76
|
recordID: string;
|
|
77
77
|
key: string;
|
|
@@ -82,6 +82,7 @@ declare class CacheInternal {
|
|
|
82
82
|
applyUpdates: boolean;
|
|
83
83
|
fields: SubscriptionSelection;
|
|
84
84
|
layer: Layer;
|
|
85
|
+
startingWith: number;
|
|
85
86
|
}): {
|
|
86
87
|
nestedIDs: LinkedList;
|
|
87
88
|
newIDs: (string | null)[];
|
|
@@ -335,6 +335,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
335
335
|
variables: variables,
|
|
336
336
|
fields: fields,
|
|
337
337
|
layer: layer,
|
|
338
|
+
startingWith: applyUpdates && update === 'append' ? oldIDs.length : 0,
|
|
338
339
|
}), newIDs = _h.newIDs, nestedIDs = _h.nestedIDs;
|
|
339
340
|
// if we're supposed to apply this write as an update, we need to figure out how
|
|
340
341
|
if (applyUpdates && update) {
|
|
@@ -655,7 +656,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
655
656
|
CacheInternal.prototype.extractNestedListIDs = function (_a) {
|
|
656
657
|
var e_9, _b;
|
|
657
658
|
var _c;
|
|
658
|
-
var value = _a.value, abstract = _a.abstract, recordID = _a.recordID, key = _a.key, linkedType = _a.linkedType, fields = _a.fields, variables = _a.variables, applyUpdates = _a.applyUpdates, specs = _a.specs, layer = _a.layer;
|
|
659
|
+
var value = _a.value, abstract = _a.abstract, recordID = _a.recordID, key = _a.key, linkedType = _a.linkedType, fields = _a.fields, variables = _a.variables, applyUpdates = _a.applyUpdates, specs = _a.specs, layer = _a.layer, startingWith = _a.startingWith;
|
|
659
660
|
// build up the two lists
|
|
660
661
|
var nestedIDs = [];
|
|
661
662
|
var newIDs = [];
|
|
@@ -675,6 +676,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
675
676
|
applyUpdates: applyUpdates,
|
|
676
677
|
specs: specs,
|
|
677
678
|
layer: layer,
|
|
679
|
+
startingWith: startingWith,
|
|
678
680
|
});
|
|
679
681
|
// add the list of new ids to our list
|
|
680
682
|
newIDs.push.apply(newIDs, __spread(inner.newIDs));
|
|
@@ -691,7 +693,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
691
693
|
// we know now that entry is an object
|
|
692
694
|
var entryObj = entry;
|
|
693
695
|
// start off building up the embedded id
|
|
694
|
-
var linkedID = recordID + "." + key + "[" + id++ + "]";
|
|
696
|
+
var linkedID = recordID + "." + key + "[" + (startingWith + id++) + "]";
|
|
695
697
|
// figure out if this is an embedded list or a linked one by looking for all of the fields marked as
|
|
696
698
|
// required to compute the entity's id
|
|
697
699
|
var embedded = ((_c = this_2.idFields(linkedType)) === null || _c === void 0 ? void 0 : _c.filter(function (field) { return typeof entry[field] === 'undefined'; }).length) > 0;
|
|
@@ -716,23 +718,6 @@ var CacheInternal = /** @class */ (function () {
|
|
|
716
718
|
return "continue";
|
|
717
719
|
}
|
|
718
720
|
}
|
|
719
|
-
// if the field is marked for pagination and we are looking at edges, we need
|
|
720
|
-
// to use the underlying node for the id because the embedded key will conflict
|
|
721
|
-
// with entries in the previous loaded value.
|
|
722
|
-
// NOTE: this approach might cause weird behavior of a node is loaded in the same
|
|
723
|
-
// location in two different pages. In practice, nodes rarely show up in the same
|
|
724
|
-
// connection so it might not be a problem.
|
|
725
|
-
if (key === 'edges' &&
|
|
726
|
-
entryObj.node &&
|
|
727
|
-
entryObj.node.__typename) {
|
|
728
|
-
var node = entryObj.node;
|
|
729
|
-
// @ts-ignore
|
|
730
|
-
var typename_1 = node.__typename;
|
|
731
|
-
var nodeID = this_2.id(typename_1, node);
|
|
732
|
-
if (nodeID) {
|
|
733
|
-
linkedID += '#' + nodeID;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
721
|
// update the linked fields too
|
|
737
722
|
this_2.writeSelection({
|
|
738
723
|
root: exports.rootID,
|
package/build/runtime/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export { query, routeQuery, componentQuery } from './query';
|
|
|
6
6
|
export { mutation } from './mutation';
|
|
7
7
|
export { fragment } from './fragment';
|
|
8
8
|
export { subscription } from './subscription';
|
|
9
|
-
export { paginatedQuery } from './pagination';
|
|
9
|
+
export { paginatedQuery, paginatedFragment } from './pagination';
|
|
10
10
|
export declare function graphql(str: TemplateStringsArray): GraphQLTagResult;
|
package/build/runtime/index.js
CHANGED
|
@@ -26,6 +26,7 @@ var subscription_1 = require("./subscription");
|
|
|
26
26
|
Object.defineProperty(exports, "subscription", { enumerable: true, get: function () { return subscription_1.subscription; } });
|
|
27
27
|
var pagination_1 = require("./pagination");
|
|
28
28
|
Object.defineProperty(exports, "paginatedQuery", { enumerable: true, get: function () { return pagination_1.paginatedQuery; } });
|
|
29
|
+
Object.defineProperty(exports, "paginatedFragment", { enumerable: true, get: function () { return pagination_1.paginatedFragment; } });
|
|
29
30
|
// this template tag gets removed by the preprocessor so it should never be invoked.
|
|
30
31
|
// this function needs to return the same value as what the preprocessor leaves behind for type consistency
|
|
31
32
|
function graphql(str) {
|
package/build/runtime/network.js
CHANGED
|
@@ -268,7 +268,6 @@ var RequestContext = /** @class */ (function () {
|
|
|
268
268
|
RequestContext.prototype.graphqlErrors = function (payload) {
|
|
269
269
|
// if we have a list of errors
|
|
270
270
|
if (payload.errors) {
|
|
271
|
-
console.log('registering graphql errors', payload.errors);
|
|
272
271
|
return this.error(500, payload.errors.map(function (_a) {
|
|
273
272
|
var message = _a.message;
|
|
274
273
|
return message;
|
|
@@ -71,7 +71,7 @@ declare class CacheInternal {
|
|
|
71
71
|
variables?: {};
|
|
72
72
|
linkedList: LinkedList;
|
|
73
73
|
}): LinkedList<GraphQLValue>;
|
|
74
|
-
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, }: {
|
|
74
|
+
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, startingWith, }: {
|
|
75
75
|
value: GraphQLValue[];
|
|
76
76
|
recordID: string;
|
|
77
77
|
key: string;
|
|
@@ -82,6 +82,7 @@ declare class CacheInternal {
|
|
|
82
82
|
applyUpdates: boolean;
|
|
83
83
|
fields: SubscriptionSelection;
|
|
84
84
|
layer: Layer;
|
|
85
|
+
startingWith: number;
|
|
85
86
|
}): {
|
|
86
87
|
nestedIDs: LinkedList;
|
|
87
88
|
newIDs: (string | null)[];
|
|
@@ -335,6 +335,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
335
335
|
variables: variables,
|
|
336
336
|
fields: fields,
|
|
337
337
|
layer: layer,
|
|
338
|
+
startingWith: applyUpdates && update === 'append' ? oldIDs.length : 0,
|
|
338
339
|
}), newIDs = _h.newIDs, nestedIDs = _h.nestedIDs;
|
|
339
340
|
// if we're supposed to apply this write as an update, we need to figure out how
|
|
340
341
|
if (applyUpdates && update) {
|
|
@@ -655,7 +656,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
655
656
|
CacheInternal.prototype.extractNestedListIDs = function (_a) {
|
|
656
657
|
var e_9, _b;
|
|
657
658
|
var _c;
|
|
658
|
-
var value = _a.value, abstract = _a.abstract, recordID = _a.recordID, key = _a.key, linkedType = _a.linkedType, fields = _a.fields, variables = _a.variables, applyUpdates = _a.applyUpdates, specs = _a.specs, layer = _a.layer;
|
|
659
|
+
var value = _a.value, abstract = _a.abstract, recordID = _a.recordID, key = _a.key, linkedType = _a.linkedType, fields = _a.fields, variables = _a.variables, applyUpdates = _a.applyUpdates, specs = _a.specs, layer = _a.layer, startingWith = _a.startingWith;
|
|
659
660
|
// build up the two lists
|
|
660
661
|
var nestedIDs = [];
|
|
661
662
|
var newIDs = [];
|
|
@@ -675,6 +676,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
675
676
|
applyUpdates: applyUpdates,
|
|
676
677
|
specs: specs,
|
|
677
678
|
layer: layer,
|
|
679
|
+
startingWith: startingWith,
|
|
678
680
|
});
|
|
679
681
|
// add the list of new ids to our list
|
|
680
682
|
newIDs.push.apply(newIDs, __spread(inner.newIDs));
|
|
@@ -691,7 +693,7 @@ var CacheInternal = /** @class */ (function () {
|
|
|
691
693
|
// we know now that entry is an object
|
|
692
694
|
var entryObj = entry;
|
|
693
695
|
// start off building up the embedded id
|
|
694
|
-
var linkedID = recordID + "." + key + "[" + id++ + "]";
|
|
696
|
+
var linkedID = recordID + "." + key + "[" + (startingWith + id++) + "]";
|
|
695
697
|
// figure out if this is an embedded list or a linked one by looking for all of the fields marked as
|
|
696
698
|
// required to compute the entity's id
|
|
697
699
|
var embedded = ((_c = this_2.idFields(linkedType)) === null || _c === void 0 ? void 0 : _c.filter(function (field) { return typeof entry[field] === 'undefined'; }).length) > 0;
|
|
@@ -716,23 +718,6 @@ var CacheInternal = /** @class */ (function () {
|
|
|
716
718
|
return "continue";
|
|
717
719
|
}
|
|
718
720
|
}
|
|
719
|
-
// if the field is marked for pagination and we are looking at edges, we need
|
|
720
|
-
// to use the underlying node for the id because the embedded key will conflict
|
|
721
|
-
// with entries in the previous loaded value.
|
|
722
|
-
// NOTE: this approach might cause weird behavior of a node is loaded in the same
|
|
723
|
-
// location in two different pages. In practice, nodes rarely show up in the same
|
|
724
|
-
// connection so it might not be a problem.
|
|
725
|
-
if (key === 'edges' &&
|
|
726
|
-
entryObj.node &&
|
|
727
|
-
entryObj.node.__typename) {
|
|
728
|
-
var node = entryObj.node;
|
|
729
|
-
// @ts-ignore
|
|
730
|
-
var typename_1 = node.__typename;
|
|
731
|
-
var nodeID = this_2.id(typename_1, node);
|
|
732
|
-
if (nodeID) {
|
|
733
|
-
linkedID += '#' + nodeID;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
721
|
// update the linked fields too
|
|
737
722
|
this_2.writeSelection({
|
|
738
723
|
root: exports.rootID,
|
|
@@ -6,5 +6,5 @@ export { query, routeQuery, componentQuery } from './query';
|
|
|
6
6
|
export { mutation } from './mutation';
|
|
7
7
|
export { fragment } from './fragment';
|
|
8
8
|
export { subscription } from './subscription';
|
|
9
|
-
export { paginatedQuery } from './pagination';
|
|
9
|
+
export { paginatedQuery, paginatedFragment } from './pagination';
|
|
10
10
|
export declare function graphql(str: TemplateStringsArray): GraphQLTagResult;
|
|
@@ -26,6 +26,7 @@ var subscription_1 = require("./subscription");
|
|
|
26
26
|
Object.defineProperty(exports, "subscription", { enumerable: true, get: function () { return subscription_1.subscription; } });
|
|
27
27
|
var pagination_1 = require("./pagination");
|
|
28
28
|
Object.defineProperty(exports, "paginatedQuery", { enumerable: true, get: function () { return pagination_1.paginatedQuery; } });
|
|
29
|
+
Object.defineProperty(exports, "paginatedFragment", { enumerable: true, get: function () { return pagination_1.paginatedFragment; } });
|
|
29
30
|
// this template tag gets removed by the preprocessor so it should never be invoked.
|
|
30
31
|
// this function needs to return the same value as what the preprocessor leaves behind for type consistency
|
|
31
32
|
function graphql(str) {
|
|
@@ -268,7 +268,6 @@ var RequestContext = /** @class */ (function () {
|
|
|
268
268
|
RequestContext.prototype.graphqlErrors = function (payload) {
|
|
269
269
|
// if we have a list of errors
|
|
270
270
|
if (payload.errors) {
|
|
271
|
-
console.log('registering graphql errors', payload.errors);
|
|
272
271
|
return this.error(500, payload.errors.map(function (_a) {
|
|
273
272
|
var message = _a.message;
|
|
274
273
|
return message;
|
|
@@ -71,7 +71,7 @@ declare class CacheInternal {
|
|
|
71
71
|
variables?: {};
|
|
72
72
|
linkedList: LinkedList;
|
|
73
73
|
}): LinkedList<GraphQLValue>;
|
|
74
|
-
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, }: {
|
|
74
|
+
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, startingWith, }: {
|
|
75
75
|
value: GraphQLValue[];
|
|
76
76
|
recordID: string;
|
|
77
77
|
key: string;
|
|
@@ -82,6 +82,7 @@ declare class CacheInternal {
|
|
|
82
82
|
applyUpdates: boolean;
|
|
83
83
|
fields: SubscriptionSelection;
|
|
84
84
|
layer: Layer;
|
|
85
|
+
startingWith: number;
|
|
85
86
|
}): {
|
|
86
87
|
nestedIDs: LinkedList;
|
|
87
88
|
newIDs: (string | null)[];
|
|
@@ -254,6 +254,7 @@ class CacheInternal {
|
|
|
254
254
|
variables: variables,
|
|
255
255
|
fields,
|
|
256
256
|
layer,
|
|
257
|
+
startingWith: applyUpdates && update === 'append' ? oldIDs.length : 0,
|
|
257
258
|
});
|
|
258
259
|
// if we're supposed to apply this write as an update, we need to figure out how
|
|
259
260
|
if (applyUpdates && update) {
|
|
@@ -492,7 +493,7 @@ class CacheInternal {
|
|
|
492
493
|
return this.getSelection({ parent: entry, selection: fields, variables });
|
|
493
494
|
});
|
|
494
495
|
}
|
|
495
|
-
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, }) {
|
|
496
|
+
extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, startingWith, }) {
|
|
496
497
|
var _a;
|
|
497
498
|
// build up the two lists
|
|
498
499
|
const nestedIDs = [];
|
|
@@ -513,6 +514,7 @@ class CacheInternal {
|
|
|
513
514
|
applyUpdates,
|
|
514
515
|
specs,
|
|
515
516
|
layer,
|
|
517
|
+
startingWith,
|
|
516
518
|
});
|
|
517
519
|
// add the list of new ids to our list
|
|
518
520
|
newIDs.push(...inner.newIDs);
|
|
@@ -529,7 +531,7 @@ class CacheInternal {
|
|
|
529
531
|
// we know now that entry is an object
|
|
530
532
|
const entryObj = entry;
|
|
531
533
|
// start off building up the embedded id
|
|
532
|
-
let linkedID = `${recordID}.${key}[${id++}]`;
|
|
534
|
+
let linkedID = `${recordID}.${key}[${startingWith + id++}]`;
|
|
533
535
|
// figure out if this is an embedded list or a linked one by looking for all of the fields marked as
|
|
534
536
|
// required to compute the entity's id
|
|
535
537
|
const embedded = ((_a = this.idFields(linkedType)) === null || _a === void 0 ? void 0 : _a.filter((field) => typeof entry[field] === 'undefined').length) > 0;
|
|
@@ -554,23 +556,6 @@ class CacheInternal {
|
|
|
554
556
|
continue;
|
|
555
557
|
}
|
|
556
558
|
}
|
|
557
|
-
// if the field is marked for pagination and we are looking at edges, we need
|
|
558
|
-
// to use the underlying node for the id because the embedded key will conflict
|
|
559
|
-
// with entries in the previous loaded value.
|
|
560
|
-
// NOTE: this approach might cause weird behavior of a node is loaded in the same
|
|
561
|
-
// location in two different pages. In practice, nodes rarely show up in the same
|
|
562
|
-
// connection so it might not be a problem.
|
|
563
|
-
if (key === 'edges' &&
|
|
564
|
-
entryObj.node &&
|
|
565
|
-
entryObj.node.__typename) {
|
|
566
|
-
const node = entryObj.node;
|
|
567
|
-
// @ts-ignore
|
|
568
|
-
const typename = node.__typename;
|
|
569
|
-
let nodeID = this.id(typename, node);
|
|
570
|
-
if (nodeID) {
|
|
571
|
-
linkedID += '#' + nodeID;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
559
|
// update the linked fields too
|
|
575
560
|
this.writeSelection({
|
|
576
561
|
root: rootID,
|
|
@@ -6,5 +6,5 @@ export { query, routeQuery, componentQuery } from './query';
|
|
|
6
6
|
export { mutation } from './mutation';
|
|
7
7
|
export { fragment } from './fragment';
|
|
8
8
|
export { subscription } from './subscription';
|
|
9
|
-
export { paginatedQuery } from './pagination';
|
|
9
|
+
export { paginatedQuery, paginatedFragment } from './pagination';
|
|
10
10
|
export declare function graphql(str: TemplateStringsArray): GraphQLTagResult;
|
|
@@ -5,7 +5,7 @@ export { query, routeQuery, componentQuery } from './query';
|
|
|
5
5
|
export { mutation } from './mutation';
|
|
6
6
|
export { fragment } from './fragment';
|
|
7
7
|
export { subscription } from './subscription';
|
|
8
|
-
export { paginatedQuery } from './pagination';
|
|
8
|
+
export { paginatedQuery, paginatedFragment } from './pagination';
|
|
9
9
|
// this template tag gets removed by the preprocessor so it should never be invoked.
|
|
10
10
|
// this function needs to return the same value as what the preprocessor leaves behind for type consistency
|
|
11
11
|
export function graphql(str) {
|
|
@@ -162,7 +162,6 @@ export class RequestContext {
|
|
|
162
162
|
graphqlErrors(payload) {
|
|
163
163
|
// if we have a list of errors
|
|
164
164
|
if (payload.errors) {
|
|
165
|
-
console.log('registering graphql errors', payload.errors);
|
|
166
165
|
return this.error(500, payload.errors.map(({ message }) => message).join('\n'));
|
|
167
166
|
}
|
|
168
167
|
return this.error(500, 'Encountered invalid response: ' + JSON.stringify(payload));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "houdini",
|
|
3
|
-
"version": "0.13.0
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "The disappearing graphql client for SvelteKit",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build:runtime": "npm run build:runtime:cjs && npm run build:runtime:esm",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"estree-walker": "^2.0.2",
|
|
50
50
|
"glob": "^7.1.6",
|
|
51
51
|
"graphql": "^15.5.0",
|
|
52
|
-
"houdini-common": "^0.13.0
|
|
52
|
+
"houdini-common": "^0.13.0",
|
|
53
53
|
"inquirer": "^7.3.3",
|
|
54
54
|
"mkdirp": "^1.0.4",
|
|
55
55
|
"node-fetch": "^2.6.1",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"rollup-plugin-preserve-shebangs": "^0.2.0",
|
|
58
58
|
"svelte": "^3.34.0"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "db48b5ab58c9c8140f54a7b0a5f9d44208a84f43"
|
|
61
61
|
}
|
package/runtime/cache/cache.ts
CHANGED
|
@@ -384,6 +384,7 @@ class CacheInternal {
|
|
|
384
384
|
variables: variables,
|
|
385
385
|
fields,
|
|
386
386
|
layer,
|
|
387
|
+
startingWith: applyUpdates && update === 'append' ? oldIDs.length : 0,
|
|
387
388
|
})
|
|
388
389
|
|
|
389
390
|
// if we're supposed to apply this write as an update, we need to figure out how
|
|
@@ -698,6 +699,7 @@ class CacheInternal {
|
|
|
698
699
|
applyUpdates,
|
|
699
700
|
specs,
|
|
700
701
|
layer,
|
|
702
|
+
startingWith,
|
|
701
703
|
}: {
|
|
702
704
|
value: GraphQLValue[]
|
|
703
705
|
recordID: string
|
|
@@ -709,6 +711,7 @@ class CacheInternal {
|
|
|
709
711
|
applyUpdates: boolean
|
|
710
712
|
fields: SubscriptionSelection
|
|
711
713
|
layer: Layer
|
|
714
|
+
startingWith: number
|
|
712
715
|
}): { nestedIDs: LinkedList; newIDs: (string | null)[] } {
|
|
713
716
|
// build up the two lists
|
|
714
717
|
const nestedIDs: LinkedList = []
|
|
@@ -731,6 +734,7 @@ class CacheInternal {
|
|
|
731
734
|
applyUpdates,
|
|
732
735
|
specs,
|
|
733
736
|
layer,
|
|
737
|
+
startingWith,
|
|
734
738
|
})
|
|
735
739
|
|
|
736
740
|
// add the list of new ids to our list
|
|
@@ -751,7 +755,7 @@ class CacheInternal {
|
|
|
751
755
|
const entryObj = entry as GraphQLObject
|
|
752
756
|
|
|
753
757
|
// start off building up the embedded id
|
|
754
|
-
let linkedID = `${recordID}.${key}[${id++}]`
|
|
758
|
+
let linkedID = `${recordID}.${key}[${startingWith + id++}]`
|
|
755
759
|
|
|
756
760
|
// figure out if this is an embedded list or a linked one by looking for all of the fields marked as
|
|
757
761
|
// required to compute the entity's id
|
|
@@ -784,26 +788,6 @@ class CacheInternal {
|
|
|
784
788
|
}
|
|
785
789
|
}
|
|
786
790
|
|
|
787
|
-
// if the field is marked for pagination and we are looking at edges, we need
|
|
788
|
-
// to use the underlying node for the id because the embedded key will conflict
|
|
789
|
-
// with entries in the previous loaded value.
|
|
790
|
-
// NOTE: this approach might cause weird behavior of a node is loaded in the same
|
|
791
|
-
// location in two different pages. In practice, nodes rarely show up in the same
|
|
792
|
-
// connection so it might not be a problem.
|
|
793
|
-
if (
|
|
794
|
-
key === 'edges' &&
|
|
795
|
-
entryObj.node &&
|
|
796
|
-
(entryObj.node as { __typename: string }).__typename
|
|
797
|
-
) {
|
|
798
|
-
const node = entryObj.node as {}
|
|
799
|
-
// @ts-ignore
|
|
800
|
-
const typename = node.__typename
|
|
801
|
-
let nodeID = this.id(typename, node)
|
|
802
|
-
if (nodeID) {
|
|
803
|
-
linkedID += '#' + nodeID
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
791
|
// update the linked fields too
|
|
808
792
|
this.writeSelection({
|
|
809
793
|
root: rootID,
|
|
@@ -464,8 +464,7 @@ test('remove from connection', function () {
|
|
|
464
464
|
expect(cache._internal_unstable.subscriptions.get('User:3', 'firstName')).toHaveLength(1)
|
|
465
465
|
// make sure we marked the corresponding edge for deletion
|
|
466
466
|
expect(
|
|
467
|
-
cache._internal_unstable.storage.topLayer.operations['User:1.friends.edges[0]
|
|
468
|
-
.deleted
|
|
467
|
+
cache._internal_unstable.storage.topLayer.operations['User:1.friends.edges[0]'].deleted
|
|
469
468
|
).toBeTruthy()
|
|
470
469
|
})
|
|
471
470
|
|
package/runtime/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { query, routeQuery, componentQuery } from './query'
|
|
|
8
8
|
export { mutation } from './mutation'
|
|
9
9
|
export { fragment } from './fragment'
|
|
10
10
|
export { subscription } from './subscription'
|
|
11
|
-
export { paginatedQuery } from './pagination'
|
|
11
|
+
export { paginatedQuery, paginatedFragment } from './pagination'
|
|
12
12
|
|
|
13
13
|
// this template tag gets removed by the preprocessor so it should never be invoked.
|
|
14
14
|
// this function needs to return the same value as what the preprocessor leaves behind for type consistency
|
package/runtime/network.ts
CHANGED
|
@@ -289,7 +289,6 @@ export class RequestContext {
|
|
|
289
289
|
graphqlErrors(payload: { errors?: GraphQLError[] }) {
|
|
290
290
|
// if we have a list of errors
|
|
291
291
|
if (payload.errors) {
|
|
292
|
-
console.log('registering graphql errors', payload.errors)
|
|
293
292
|
return this.error(500, payload.errors.map(({ message }) => message).join('\n'))
|
|
294
293
|
}
|
|
295
294
|
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { SubscriptionSelection, ListWhen, SubscriptionSpec } from '../types';
|
|
2
|
-
import { Cache } from './cache';
|
|
3
|
-
import { Record } from './record';
|
|
4
|
-
export declare class ListHandler {
|
|
5
|
-
readonly record: Record;
|
|
6
|
-
readonly key: string;
|
|
7
|
-
readonly listType: string;
|
|
8
|
-
private cache;
|
|
9
|
-
readonly selection: SubscriptionSelection;
|
|
10
|
-
private _when?;
|
|
11
|
-
private filters?;
|
|
12
|
-
readonly name: string;
|
|
13
|
-
readonly parentID: SubscriptionSpec['parentID'];
|
|
14
|
-
private connection;
|
|
15
|
-
constructor({ name, cache, record, key, listType, selection, when, filters, parentID, connection, }: {
|
|
16
|
-
name: string;
|
|
17
|
-
connection: boolean;
|
|
18
|
-
cache: Cache;
|
|
19
|
-
record: Record;
|
|
20
|
-
key: string;
|
|
21
|
-
listType: string;
|
|
22
|
-
selection: SubscriptionSelection;
|
|
23
|
-
when?: ListWhen;
|
|
24
|
-
filters?: ListHandler['filters'];
|
|
25
|
-
parentID?: SubscriptionSpec['parentID'];
|
|
26
|
-
});
|
|
27
|
-
when(when?: ListWhen): ListHandler;
|
|
28
|
-
append(selection: SubscriptionSelection, data: {}, variables?: {}): void;
|
|
29
|
-
prepend(selection: SubscriptionSelection, data: {}, variables?: {}): void;
|
|
30
|
-
addToList(selection: SubscriptionSelection, data: {}, variables: {}, where: 'first' | 'last'): void;
|
|
31
|
-
removeID(id: string, variables?: {}): void;
|
|
32
|
-
remove(data: {}, variables?: {}): void;
|
|
33
|
-
private validateWhen;
|
|
34
|
-
[Symbol.iterator](): Generator<Record, void, unknown>;
|
|
35
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
export class ListHandler {
|
|
2
|
-
constructor({ name, cache, record, key, listType, selection, when, filters, parentID, connection, }) {
|
|
3
|
-
this.record = record;
|
|
4
|
-
this.key = key;
|
|
5
|
-
this.listType = listType;
|
|
6
|
-
this.cache = cache;
|
|
7
|
-
this.selection = selection;
|
|
8
|
-
this._when = when;
|
|
9
|
-
this.filters = filters;
|
|
10
|
-
this.name = name;
|
|
11
|
-
this.parentID = parentID;
|
|
12
|
-
this.connection = connection;
|
|
13
|
-
}
|
|
14
|
-
// when applies a when condition to a new list pointing to the same spot
|
|
15
|
-
when(when) {
|
|
16
|
-
return new ListHandler({
|
|
17
|
-
cache: this.cache,
|
|
18
|
-
record: this.record,
|
|
19
|
-
key: this.key,
|
|
20
|
-
listType: this.listType,
|
|
21
|
-
selection: this.selection,
|
|
22
|
-
when,
|
|
23
|
-
filters: this.filters,
|
|
24
|
-
parentID: this.parentID,
|
|
25
|
-
name: this.name,
|
|
26
|
-
connection: this.connection,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
append(selection, data, variables = {}) {
|
|
30
|
-
return this.addToList(selection, data, variables, 'last');
|
|
31
|
-
}
|
|
32
|
-
prepend(selection, data, variables = {}) {
|
|
33
|
-
return this.addToList(selection, data, variables, 'first');
|
|
34
|
-
}
|
|
35
|
-
addToList(selection, data, variables = {}, where) {
|
|
36
|
-
// figure out the id of the type we are adding
|
|
37
|
-
const dataID = this.cache.id(this.listType, data);
|
|
38
|
-
// if there are conditions for this operation
|
|
39
|
-
if (!this.validateWhen() || !dataID) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
// we are going to implement the insert as a write with an update flag on a field
|
|
43
|
-
// that matches the key of the list. We'll have to embed the lists data and selection
|
|
44
|
-
// in the appropriate objects
|
|
45
|
-
let insertSelection = selection;
|
|
46
|
-
let insertData = data;
|
|
47
|
-
// if we are wrapping a connection, we have to embed the data under edges > node
|
|
48
|
-
if (this.connection) {
|
|
49
|
-
insertSelection = {
|
|
50
|
-
newEntry: {
|
|
51
|
-
keyRaw: this.key,
|
|
52
|
-
type: 'Connection',
|
|
53
|
-
fields: {
|
|
54
|
-
edges: {
|
|
55
|
-
keyRaw: 'edges',
|
|
56
|
-
type: 'ConnectionEdge',
|
|
57
|
-
update: (where === 'first' ? 'prepend' : 'append'),
|
|
58
|
-
fields: {
|
|
59
|
-
node: {
|
|
60
|
-
type: this.listType,
|
|
61
|
-
keyRaw: 'node',
|
|
62
|
-
fields: {
|
|
63
|
-
...selection,
|
|
64
|
-
__typename: {
|
|
65
|
-
keyRaw: '__typename',
|
|
66
|
-
type: 'String',
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
insertData = {
|
|
76
|
-
newEntry: {
|
|
77
|
-
edges: [{ node: { ...data, __typename: this.listType } }],
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
insertSelection = {
|
|
83
|
-
newEntries: {
|
|
84
|
-
keyRaw: this.key,
|
|
85
|
-
type: this.listType,
|
|
86
|
-
update: (where === 'first' ? 'prepend' : 'append'),
|
|
87
|
-
fields: {
|
|
88
|
-
...selection,
|
|
89
|
-
__typename: {
|
|
90
|
-
keyRaw: '__typename',
|
|
91
|
-
type: 'String',
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
insertData = {
|
|
97
|
-
newEntries: [{ ...data, __typename: this.listType }],
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
// get the list of specs that are subscribing to the list
|
|
101
|
-
const subscribers = this.record.getSubscribers(this.key);
|
|
102
|
-
// look up the new record in the cache
|
|
103
|
-
const newRecord = this.cache.internal.record(dataID);
|
|
104
|
-
// walk down the list fields relative to the new record
|
|
105
|
-
// and make sure all of the list's subscribers are listening
|
|
106
|
-
// to that object
|
|
107
|
-
this.cache.internal.insertSubscribers(newRecord, selection, variables, ...subscribers);
|
|
108
|
-
// update the cache with the data we just found
|
|
109
|
-
this.cache.write({
|
|
110
|
-
selection: insertSelection,
|
|
111
|
-
data: insertData,
|
|
112
|
-
variables,
|
|
113
|
-
parent: this.record.id,
|
|
114
|
-
applyUpdates: true,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
removeID(id, variables = {}) {
|
|
118
|
-
// if there are conditions for this operation
|
|
119
|
-
if (!this.validateWhen()) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
// if we are removing from a connection, the id we are removing from
|
|
123
|
-
// has to be computed
|
|
124
|
-
let parentID = this.record.id;
|
|
125
|
-
let targetID = id;
|
|
126
|
-
let targetKey = this.key;
|
|
127
|
-
// if we are removing a record from a connection we have to walk through
|
|
128
|
-
// some embedded references first
|
|
129
|
-
if (this.connection) {
|
|
130
|
-
const embeddedConnection = this.record.linkedRecord(this.key);
|
|
131
|
-
if (!embeddedConnection) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
// look at every embedded edge for the one with a node corresponding to the element
|
|
135
|
-
// we want to delete
|
|
136
|
-
for (const edge of embeddedConnection.flatLinkedList('edges') || []) {
|
|
137
|
-
if (!edge) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
// look at the edge's node
|
|
141
|
-
const node = edge.linkedRecord('node');
|
|
142
|
-
if (!node) {
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
// if we found the node
|
|
146
|
-
if (node.id === id) {
|
|
147
|
-
targetID = edge.id;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
parentID = embeddedConnection.id;
|
|
151
|
-
targetKey = 'edges';
|
|
152
|
-
}
|
|
153
|
-
// get the list of specs that are subscribing to the list
|
|
154
|
-
const subscribers = this.record.getSubscribers(this.key);
|
|
155
|
-
// disconnect record from any subscriptions associated with the list
|
|
156
|
-
this.cache.internal.unsubscribeSelection(this.cache.internal.record(targetID),
|
|
157
|
-
// if we're unsubscribing from a connection, only unsubscribe from the target
|
|
158
|
-
this.connection ? this.selection.edges.fields : this.selection, variables, ...subscribers.map(({ set }) => set));
|
|
159
|
-
// remove the target from the parent
|
|
160
|
-
this.cache.internal.record(parentID).removeFromLinkedList(targetKey, targetID);
|
|
161
|
-
// notify the subscribers about the change
|
|
162
|
-
this.cache.internal.notifySubscribers(subscribers, variables);
|
|
163
|
-
// if we are removing from a connection, delete the embedded edge holding the record
|
|
164
|
-
if (this.connection) {
|
|
165
|
-
this.cache.internal.deleteID(targetID);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
remove(data, variables = {}) {
|
|
169
|
-
const targetID = this.cache.id(this.listType, data);
|
|
170
|
-
if (!targetID) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
// figure out the id of the type we are adding
|
|
174
|
-
this.removeID(targetID, variables);
|
|
175
|
-
}
|
|
176
|
-
validateWhen() {
|
|
177
|
-
// if this when doesn't apply, we should look at others to see if we should update those behind the scenes
|
|
178
|
-
let ok = true;
|
|
179
|
-
// if there are conditions for this operation
|
|
180
|
-
if (this._when) {
|
|
181
|
-
// we only NEED there to be target filters for must's
|
|
182
|
-
const targets = this.filters;
|
|
183
|
-
// check must's first
|
|
184
|
-
if (this._when.must && targets) {
|
|
185
|
-
ok = Object.entries(this._when.must).reduce((prev, [key, value]) => Boolean(prev && targets[key] == value), ok);
|
|
186
|
-
}
|
|
187
|
-
// if there are no targets, nothing could be true that can we compare against
|
|
188
|
-
if (this._when.must_not) {
|
|
189
|
-
ok =
|
|
190
|
-
!targets ||
|
|
191
|
-
Object.entries(this._when.must_not).reduce((prev, [key, value]) => Boolean(prev && targets[key] != value), ok);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return ok;
|
|
195
|
-
}
|
|
196
|
-
// iterating over the list handler should be the same as iterating over
|
|
197
|
-
// the underlying linked list
|
|
198
|
-
*[Symbol.iterator]() {
|
|
199
|
-
for (let record of this.record.flatLinkedList(this.key)) {
|
|
200
|
-
yield record;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { GraphQLValue, Maybe, SubscriptionSpec } from '../types';
|
|
2
|
-
import { Cache, LinkedList } from './cache';
|
|
3
|
-
export declare class Record {
|
|
4
|
-
fields: {
|
|
5
|
-
[key: string]: GraphQLValue;
|
|
6
|
-
};
|
|
7
|
-
keyVersions: {
|
|
8
|
-
[key: string]: Set<string>;
|
|
9
|
-
};
|
|
10
|
-
readonly id: string;
|
|
11
|
-
private subscribers;
|
|
12
|
-
private recordLinks;
|
|
13
|
-
listLinks: {
|
|
14
|
-
[key: string]: LinkedList;
|
|
15
|
-
};
|
|
16
|
-
private cache;
|
|
17
|
-
private referenceCounts;
|
|
18
|
-
private lifetimes;
|
|
19
|
-
constructor(cache: Cache, id: string);
|
|
20
|
-
allSubscribers(): SubscriptionSpec[];
|
|
21
|
-
getField(fieldName: string): GraphQLValue;
|
|
22
|
-
writeField(fieldName: string, value: GraphQLValue): void;
|
|
23
|
-
writeRecordLink(fieldName: string, value: string | null): void;
|
|
24
|
-
writeListLink(fieldName: string, value: LinkedList): void;
|
|
25
|
-
linkedRecord(fieldName: string): Record;
|
|
26
|
-
linkedRecordID(fieldName: string): string;
|
|
27
|
-
linkedListIDs(fieldName: string): (string | null)[];
|
|
28
|
-
flatLinkedList(fieldName: string): Maybe<Record>[];
|
|
29
|
-
appendLinkedList(fieldName: string, id: string): void;
|
|
30
|
-
prependLinkedList(fieldName: string, id: string): void;
|
|
31
|
-
removeFromLinkedList(fieldName: string, id: string): void;
|
|
32
|
-
addSubscriber(rawKey: string, key: string, ...specs: SubscriptionSpec[]): void;
|
|
33
|
-
getSubscribers(fieldName: string): SubscriptionSpec[];
|
|
34
|
-
forgetSubscribers(...targets: SubscriptionSpec[]): void;
|
|
35
|
-
removeAllSubscribers(): void;
|
|
36
|
-
removeAllSubscriptionVersions(keyRaw: string, spec: SubscriptionSpec): void;
|
|
37
|
-
private forgetSubscribers_walk;
|
|
38
|
-
removeSubscribers(fields: string[], sets: SubscriptionSpec['set'][]): void;
|
|
39
|
-
onGcTick(): void;
|
|
40
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
// for the most part, this is a very low-level/dumb class that is meant to track state related
|
|
2
|
-
// to a specific entity in the cached graph.
|
|
3
|
-
export class Record {
|
|
4
|
-
constructor(cache, id) {
|
|
5
|
-
this.fields = {};
|
|
6
|
-
this.keyVersions = {};
|
|
7
|
-
this.subscribers = {};
|
|
8
|
-
this.recordLinks = {};
|
|
9
|
-
this.listLinks = {};
|
|
10
|
-
this.referenceCounts = {};
|
|
11
|
-
this.lifetimes = new Map();
|
|
12
|
-
this.cache = cache;
|
|
13
|
-
this.id = id;
|
|
14
|
-
}
|
|
15
|
-
allSubscribers() {
|
|
16
|
-
return Object.values(this.subscribers).flatMap((subscribers) => subscribers);
|
|
17
|
-
}
|
|
18
|
-
getField(fieldName) {
|
|
19
|
-
return this.fields[fieldName];
|
|
20
|
-
}
|
|
21
|
-
writeField(fieldName, value) {
|
|
22
|
-
this.fields[fieldName] = value;
|
|
23
|
-
}
|
|
24
|
-
writeRecordLink(fieldName, value) {
|
|
25
|
-
this.recordLinks[fieldName] = value;
|
|
26
|
-
}
|
|
27
|
-
writeListLink(fieldName, value) {
|
|
28
|
-
this.listLinks[fieldName] = value;
|
|
29
|
-
}
|
|
30
|
-
linkedRecord(fieldName) {
|
|
31
|
-
const linkedRecord = this.recordLinks[fieldName];
|
|
32
|
-
if (linkedRecord === null) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
return this.cache.internal.getRecord(linkedRecord);
|
|
36
|
-
}
|
|
37
|
-
linkedRecordID(fieldName) {
|
|
38
|
-
return this.recordLinks[fieldName];
|
|
39
|
-
}
|
|
40
|
-
linkedListIDs(fieldName) {
|
|
41
|
-
const ids = [];
|
|
42
|
-
// we need to flatten the list links
|
|
43
|
-
const unvisited = [this.listLinks[fieldName] || []];
|
|
44
|
-
while (unvisited.length > 0) {
|
|
45
|
-
const target = unvisited.shift();
|
|
46
|
-
for (const id of target) {
|
|
47
|
-
if (Array.isArray(id)) {
|
|
48
|
-
unvisited.push(id);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
ids.push(id);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return ids;
|
|
55
|
-
}
|
|
56
|
-
flatLinkedList(fieldName) {
|
|
57
|
-
return this.linkedListIDs(fieldName).map(this.cache.internal.getRecord);
|
|
58
|
-
}
|
|
59
|
-
appendLinkedList(fieldName, id) {
|
|
60
|
-
// this could be the first time we've seen the list
|
|
61
|
-
if (!this.listLinks[fieldName]) {
|
|
62
|
-
this.listLinks[fieldName] = [];
|
|
63
|
-
}
|
|
64
|
-
this.listLinks[fieldName].push(id);
|
|
65
|
-
}
|
|
66
|
-
prependLinkedList(fieldName, id) {
|
|
67
|
-
// this could be the first time we've seen the list
|
|
68
|
-
if (!this.listLinks[fieldName]) {
|
|
69
|
-
this.listLinks[fieldName] = [];
|
|
70
|
-
}
|
|
71
|
-
this.listLinks[fieldName].unshift(id);
|
|
72
|
-
}
|
|
73
|
-
removeFromLinkedList(fieldName, id) {
|
|
74
|
-
this.listLinks[fieldName] = (this.listLinks[fieldName] || []).filter((link) => link !== id);
|
|
75
|
-
}
|
|
76
|
-
addSubscriber(rawKey, key, ...specs) {
|
|
77
|
-
// if this is the first time we've seen the raw key
|
|
78
|
-
if (!this.keyVersions[rawKey]) {
|
|
79
|
-
this.keyVersions[rawKey] = new Set();
|
|
80
|
-
}
|
|
81
|
-
// add this version of the key if we need to
|
|
82
|
-
this.keyVersions[rawKey].add(key);
|
|
83
|
-
// the existing list
|
|
84
|
-
const existingSubscribers = (this.subscribers[key] || []).map(({ set }) => set);
|
|
85
|
-
// the list of new subscribers
|
|
86
|
-
const newSubscribers = specs.filter(({ set }) => !existingSubscribers.includes(set));
|
|
87
|
-
this.subscribers[key] = this.getSubscribers(key).concat(...newSubscribers);
|
|
88
|
-
// if this is the first time we've seen this key
|
|
89
|
-
if (!this.referenceCounts[key]) {
|
|
90
|
-
this.referenceCounts[key] = new Map();
|
|
91
|
-
}
|
|
92
|
-
const counts = this.referenceCounts[key];
|
|
93
|
-
// clear the lifetime count for the field
|
|
94
|
-
this.lifetimes.delete(key);
|
|
95
|
-
// increment the reference count for every subscriber
|
|
96
|
-
for (const spec of specs) {
|
|
97
|
-
// we're going to increment the current value by one
|
|
98
|
-
counts.set(spec.set, (counts.get(spec.set) || 0) + 1);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
getSubscribers(fieldName) {
|
|
102
|
-
return this.subscribers[fieldName] || [];
|
|
103
|
-
}
|
|
104
|
-
forgetSubscribers(...targets) {
|
|
105
|
-
this.forgetSubscribers_walk(targets.map(({ set }) => set));
|
|
106
|
-
}
|
|
107
|
-
removeAllSubscribers() {
|
|
108
|
-
this.forgetSubscribers(...this.allSubscribers());
|
|
109
|
-
}
|
|
110
|
-
removeAllSubscriptionVersions(keyRaw, spec) {
|
|
111
|
-
// visit every version of the key we've seen and remove the spec from the list of subscribers
|
|
112
|
-
const versions = this.keyVersions[keyRaw];
|
|
113
|
-
// if there are no known versions, we're done
|
|
114
|
-
if (!versions) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
this.removeSubscribers([...this.keyVersions[keyRaw]], [spec.set]);
|
|
118
|
-
}
|
|
119
|
-
forgetSubscribers_walk(targets, visited = []) {
|
|
120
|
-
var _a;
|
|
121
|
-
// clean up any subscribers that reference the set
|
|
122
|
-
this.removeSubscribers(Object.keys(this.subscribers), targets);
|
|
123
|
-
visited.push(this.id);
|
|
124
|
-
// walk down to every record we know about
|
|
125
|
-
const linkedIDs = Object.keys(this.recordLinks);
|
|
126
|
-
for (const key of Object.keys(this.listLinks)) {
|
|
127
|
-
for (const child of this.linkedListIDs(key)) {
|
|
128
|
-
if (child !== null) {
|
|
129
|
-
linkedIDs.push(child);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
for (const linkedRecordID of linkedIDs) {
|
|
134
|
-
if (visited.includes(linkedRecordID)) {
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
(_a = this.cache.internal.getRecord(linkedRecordID)) === null || _a === void 0 ? void 0 : _a.forgetSubscribers_walk(targets, visited);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
removeSubscribers(fields, sets) {
|
|
141
|
-
var _a;
|
|
142
|
-
// clean up any subscribers that reference the set
|
|
143
|
-
for (const fieldName of fields) {
|
|
144
|
-
// build up a list of the sets we actually need to remove after
|
|
145
|
-
// checking reference counts
|
|
146
|
-
let targets = [];
|
|
147
|
-
for (const set of sets) {
|
|
148
|
-
// if we dont know this field/set combo, there's nothing to do (probably a bug somewhere)
|
|
149
|
-
if (!((_a = this.referenceCounts[fieldName]) === null || _a === void 0 ? void 0 : _a.has(set))) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
const counts = this.referenceCounts[fieldName];
|
|
153
|
-
const newVal = (counts.get(set) || 0) - 1;
|
|
154
|
-
// decrement the reference of every field
|
|
155
|
-
counts.set(set, newVal);
|
|
156
|
-
// if that was the last reference we knew of
|
|
157
|
-
if (newVal <= 0) {
|
|
158
|
-
targets.push(set);
|
|
159
|
-
// remove the reference to the set function
|
|
160
|
-
counts.delete(set);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// we do need to remove the set from the list
|
|
164
|
-
this.subscribers[fieldName] = this.getSubscribers(fieldName).filter(({ set }) => !targets.includes(set));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// this function is invoked by the cache when the garbage collector is ticking
|
|
168
|
-
onGcTick() {
|
|
169
|
-
const fields = Object.keys(this.fields)
|
|
170
|
-
.concat(Object.keys(this.listLinks))
|
|
171
|
-
.concat(Object.keys(this.recordLinks));
|
|
172
|
-
// any fields with no reference counts need to be decremented
|
|
173
|
-
for (const field of fields) {
|
|
174
|
-
// {value} has a key for every subscriber - can check for no subscribers by looking
|
|
175
|
-
// at the number of fields in the value
|
|
176
|
-
if (!this.referenceCounts[field] || this.referenceCounts[field].size === 0) {
|
|
177
|
-
// lower the current count by 1
|
|
178
|
-
const previousCount = this.lifetimes.get(field) || 0;
|
|
179
|
-
this.lifetimes.set(field, previousCount + 1);
|
|
180
|
-
// if the lifetime exceeds the cache's buffer size we should remove the field, linked record, or list
|
|
181
|
-
if (this.lifetimes.get(field) > this.cache.cacheBufferSize) {
|
|
182
|
-
delete this.fields[field];
|
|
183
|
-
delete this.recordLinks[field];
|
|
184
|
-
delete this.recordLinks[field];
|
|
185
|
-
// if we dont have any data left, delete the record from the cache
|
|
186
|
-
if (Object.keys(this.fields).length === 0 &&
|
|
187
|
-
Object.keys(this.recordLinks).length === 0 &&
|
|
188
|
-
Object.keys(this.recordLinks).length === 0) {
|
|
189
|
-
this.cache.internal.deleteID(this.id);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|