@n1ru4l/in-memory-live-query-store
Advanced tools
Comparing version 0.9.0-alpha-51b566b.0 to 0.9.0-alpha-e02cfcd.0
148
esm/index.js
import { visit, visitWithTypeInfo, isNonNullType, isScalarType, execute, getOperationAST, GraphQLError, defaultFieldResolver, TypeInfo } from 'graphql'; | ||
import { mapSchema, MapperKind } from '@graphql-tools/utils'; | ||
import { makePushPullAsyncIterableIterator, isAsyncIterable } from '@n1ru4l/push-pull-async-iterable-iterator'; | ||
import { mapSchema, MapperKind, isAsyncIterable } from '@graphql-tools/utils'; | ||
import { Repeater } from '@repeaterjs/repeater'; | ||
import { getLiveDirectiveNode, getLiveDirectiveArgumentValues } from '@n1ru4l/graphql-live-query'; | ||
@@ -176,3 +176,2 @@ import { getArgumentValues } from 'graphql/execution/values.js'; | ||
var _a, _b; | ||
const ORIGINAL_CONTEXT_SYMBOL = Symbol("ORIGINAL_CONTEXT"); | ||
@@ -186,34 +185,16 @@ const addResourceIdentifierCollectorToSchema = (schema, idFieldName) => mapSchema(schema, { | ||
newFieldConfig.resolve = (src, args, context, info) => { | ||
var _a, _b; | ||
var _a; | ||
if (!context || ORIGINAL_CONTEXT_SYMBOL in context === false) { | ||
return resolve(src, args, context, info); | ||
} | ||
const liveQueyContext = context; | ||
const result = resolve(src, args, liveQueyContext[ORIGINAL_CONTEXT_SYMBOL], info); | ||
const collectResourceIdentifier = context.collectResourceIdentifier; | ||
const addResourceIdentifier = context.addResourceIdentifier; | ||
context = context[ORIGINAL_CONTEXT_SYMBOL]; | ||
const result = resolve(src, args, context, info); | ||
const fieldConfigExtensions = fieldConfig.extensions; | ||
if ((_a = fieldConfigExtensions === null || fieldConfigExtensions === void 0 ? void 0 : fieldConfigExtensions.liveQuery) === null || _a === void 0 ? void 0 : _a.collectResourceIdentifiers) { | ||
liveQueyContext.addResourceIdentifier(fieldConfigExtensions.liveQuery.collectResourceIdentifiers(src, args)); | ||
addResourceIdentifier(fieldConfigExtensions.liveQuery.collectResourceIdentifiers(src, args)); | ||
} | ||
const fieldCoordinate = `${typename}.${fieldName}`; | ||
const indicesForCoordinate = (_b = liveQueyContext.indices) === null || _b === void 0 ? void 0 : _b.get(fieldCoordinate); | ||
if (indicesForCoordinate) { | ||
for (const index of indicesForCoordinate) { | ||
let parts = []; | ||
for (const part of index) { | ||
if (Array.isArray(part)) { | ||
if (args[part[0]] === part[1]) { | ||
parts.push(`${part[0]}:"${args[part[0]]}"`); | ||
} | ||
} | ||
else if (args[part] !== undefined) { | ||
parts.push(`${part}:"${args[part]}"`); | ||
} | ||
} | ||
if (parts.length) { | ||
liveQueyContext.addResourceIdentifier(`${fieldCoordinate}(${parts.join(",")})`); | ||
} | ||
} | ||
} | ||
if (isIDField) { | ||
runWith(result, (id) => liveQueyContext.collectResourceIdentifier({ typename, id })); | ||
runWith(result, (id) => collectResourceIdentifier({ typename, id })); | ||
} | ||
@@ -226,5 +207,2 @@ return result; | ||
const defaultResourceIdentifierNormalizer = (params) => `${params.typename}:${params.id}`; | ||
const nextTick = (_b = (_a = (typeof process === "object" && typeof process.nextTick === "function" | ||
? process.nextTick | ||
: undefined)) !== null && _a !== void 0 ? _a : setImmediate) !== null && _b !== void 0 ? _b : setTimeout; | ||
class InMemoryLiveQueryStore { | ||
@@ -239,3 +217,2 @@ constructor(params) { | ||
this._idFieldName = "id"; | ||
this._indices = null; | ||
this.makeExecute = (execute) => (args) => { | ||
@@ -286,14 +263,22 @@ const { schema: inputSchema, document, rootValue, contextValue, variableValues, operationName, ...additionalArguments } = args; | ||
})); | ||
const { asyncIterableIterator: iterator, pushValue } = makePushPullAsyncIterableIterator(); | ||
// keep track that current execution is the latest in order to prevent race-conditions :) | ||
let executionCounter = 0; | ||
let previousIdentifier = new Set(rootFieldIdentifier); | ||
const record = { | ||
iterator, | ||
pushValue, | ||
run: () => { | ||
const context = this; | ||
return new Repeater(async function liveQueryRepeater(push, onStop) { | ||
// utils for throttle | ||
let cancelThrottle; | ||
let run; | ||
let executionCounter = 0; | ||
let previousIdentifier = new Set(rootFieldIdentifier); | ||
function scheduleRun() { | ||
run(); | ||
} | ||
function dispose() { | ||
cancelThrottle === null || cancelThrottle === void 0 ? void 0 : cancelThrottle(); | ||
context._resourceTracker.release(scheduleRun, previousIdentifier); | ||
} | ||
onStop.then(dispose); | ||
run = function run() { | ||
executionCounter = executionCounter + 1; | ||
const counter = executionCounter; | ||
const newIdentifier = new Set(rootFieldIdentifier); | ||
const collectResourceIdentifier = (parameter) => newIdentifier.add(this._buildResourceIdentifier(parameter)); | ||
const collectResourceIdentifier = (parameter) => newIdentifier.add(context._buildResourceIdentifier(parameter)); | ||
const addResourceIdentifier = (values) => { | ||
@@ -311,8 +296,2 @@ if (isNone(values)) { | ||
}; | ||
const context = { | ||
[ORIGINAL_CONTEXT_SYMBOL]: contextValue, | ||
collectResourceIdentifier, | ||
addResourceIdentifier, | ||
indices: this._indices, | ||
}; | ||
const result = execute({ | ||
@@ -323,3 +302,7 @@ schema, | ||
rootValue, | ||
contextValue: context, | ||
contextValue: { | ||
[ORIGINAL_CONTEXT_SYMBOL]: contextValue, | ||
collectResourceIdentifier, | ||
addResourceIdentifier, | ||
}, | ||
variableValues, | ||
@@ -329,30 +312,13 @@ ...additionalArguments, | ||
}); | ||
// result cannot be a AsyncIterableIterator if the `NoLiveMixedWithDeferStreamRule` was used. | ||
// in case anyone forgot to add it we just panic and stop the execution :) | ||
const handleAsyncIterator = (iterator) => { | ||
var _a; | ||
(_a = iterator.return) === null || _a === void 0 ? void 0 : _a.call(iterator); | ||
record.pushValue({ | ||
errors: [ | ||
new GraphQLError(`"execute" returned a AsyncIterator instead of a MaybePromise<ExecutionResult>. The "NoLiveMixedWithDeferStreamRule" rule might have been skipped.`), | ||
], | ||
}); | ||
// delay to next tick to ensure the error is delivered to listeners. | ||
// TODO: figure out whether there is a better way for doing this. | ||
nextTick(() => { | ||
record.iterator.return(); | ||
}); | ||
this._resourceTracker.release(record, previousIdentifier); | ||
}; | ||
runWith(result, (result) => { | ||
if (isAsyncIterable(result)) { | ||
handleAsyncIterator(result); | ||
onStop(new Error(`"execute" returned a AsyncIterator instead of a MaybePromise<ExecutionResult>. The "NoLiveMixedWithDeferStreamRule" rule might have been skipped.`)); | ||
return; | ||
} | ||
if (counter === executionCounter) { | ||
this._resourceTracker.track(record, previousIdentifier, newIdentifier); | ||
context._resourceTracker.track(scheduleRun, previousIdentifier, newIdentifier); | ||
previousIdentifier = newIdentifier; | ||
const liveResult = result; | ||
liveResult.isLive = true; | ||
if (this._includeIdentifierExtension === true) { | ||
if (context._includeIdentifierExtension === true) { | ||
if (!liveResult.extensions) { | ||
@@ -364,25 +330,14 @@ liveResult.extensions = {}; | ||
} | ||
record.pushValue(liveResult); | ||
push(liveResult); | ||
} | ||
}); | ||
}, | ||
}; | ||
// utils for throttle | ||
let cancelThrottle; | ||
if (isSome(throttleValue)) { | ||
const { run, cancel } = throttle(record.run, throttleValue); | ||
record.run = run; | ||
cancelThrottle = cancel; | ||
} | ||
this._resourceTracker.register(record, previousIdentifier); | ||
// Execute initial query | ||
record.run(); | ||
// TODO: figure out how we can do this stuff without monkey-patching the iterator | ||
const originalReturn = iterator.return.bind(iterator); | ||
iterator.return = () => { | ||
cancelThrottle === null || cancelThrottle === void 0 ? void 0 : cancelThrottle(); | ||
this._resourceTracker.release(record, previousIdentifier); | ||
return originalReturn(); | ||
}; | ||
return iterator; | ||
}; | ||
if (isSome(throttleValue)) { | ||
const throttled = throttle(run, throttleValue); | ||
run = throttled.run; | ||
cancelThrottle = throttled.cancel; | ||
} | ||
context._resourceTracker.register(scheduleRun, previousIdentifier); | ||
scheduleRun(); | ||
}); | ||
}; | ||
@@ -403,13 +358,2 @@ /** @deprecated Please use InMemoryLiveQueryStore.makeExecute instead. */ | ||
} | ||
if (params === null || params === void 0 ? void 0 : params.indexBy) { | ||
this._indices = new Map(); | ||
for (const { field, args } of params.indexBy) { | ||
let indices = this._indices.get(field); | ||
if (!indices) { | ||
indices = []; | ||
this._indices.set(field, indices); | ||
} | ||
indices.push(args); | ||
} | ||
} | ||
this._includeIdentifierExtension = | ||
@@ -441,4 +385,4 @@ (_a = params === null || params === void 0 ? void 0 : params.includeIdentifierExtension) !== null && _a !== void 0 ? _a : (typeof process === "undefined" | ||
const records = this._resourceTracker.getRecordsForIdentifiers(identifiers); | ||
for (const record of records) { | ||
record.run(); | ||
for (const run of records) { | ||
run(); | ||
} | ||
@@ -445,0 +389,0 @@ } |
148
index.js
@@ -7,3 +7,3 @@ 'use strict'; | ||
const utils = require('@graphql-tools/utils'); | ||
const pushPullAsyncIterableIterator = require('@n1ru4l/push-pull-async-iterable-iterator'); | ||
const repeater = require('@repeaterjs/repeater'); | ||
const graphqlLiveQuery = require('@n1ru4l/graphql-live-query'); | ||
@@ -181,3 +181,2 @@ const values_js = require('graphql/execution/values.js'); | ||
var _a, _b; | ||
const ORIGINAL_CONTEXT_SYMBOL = Symbol("ORIGINAL_CONTEXT"); | ||
@@ -191,34 +190,16 @@ const addResourceIdentifierCollectorToSchema = (schema, idFieldName) => utils.mapSchema(schema, { | ||
newFieldConfig.resolve = (src, args, context, info) => { | ||
var _a, _b; | ||
var _a; | ||
if (!context || ORIGINAL_CONTEXT_SYMBOL in context === false) { | ||
return resolve(src, args, context, info); | ||
} | ||
const liveQueyContext = context; | ||
const result = resolve(src, args, liveQueyContext[ORIGINAL_CONTEXT_SYMBOL], info); | ||
const collectResourceIdentifier = context.collectResourceIdentifier; | ||
const addResourceIdentifier = context.addResourceIdentifier; | ||
context = context[ORIGINAL_CONTEXT_SYMBOL]; | ||
const result = resolve(src, args, context, info); | ||
const fieldConfigExtensions = fieldConfig.extensions; | ||
if ((_a = fieldConfigExtensions === null || fieldConfigExtensions === void 0 ? void 0 : fieldConfigExtensions.liveQuery) === null || _a === void 0 ? void 0 : _a.collectResourceIdentifiers) { | ||
liveQueyContext.addResourceIdentifier(fieldConfigExtensions.liveQuery.collectResourceIdentifiers(src, args)); | ||
addResourceIdentifier(fieldConfigExtensions.liveQuery.collectResourceIdentifiers(src, args)); | ||
} | ||
const fieldCoordinate = `${typename}.${fieldName}`; | ||
const indicesForCoordinate = (_b = liveQueyContext.indices) === null || _b === void 0 ? void 0 : _b.get(fieldCoordinate); | ||
if (indicesForCoordinate) { | ||
for (const index of indicesForCoordinate) { | ||
let parts = []; | ||
for (const part of index) { | ||
if (Array.isArray(part)) { | ||
if (args[part[0]] === part[1]) { | ||
parts.push(`${part[0]}:"${args[part[0]]}"`); | ||
} | ||
} | ||
else if (args[part] !== undefined) { | ||
parts.push(`${part}:"${args[part]}"`); | ||
} | ||
} | ||
if (parts.length) { | ||
liveQueyContext.addResourceIdentifier(`${fieldCoordinate}(${parts.join(",")})`); | ||
} | ||
} | ||
} | ||
if (isIDField) { | ||
runWith(result, (id) => liveQueyContext.collectResourceIdentifier({ typename, id })); | ||
runWith(result, (id) => collectResourceIdentifier({ typename, id })); | ||
} | ||
@@ -231,5 +212,2 @@ return result; | ||
const defaultResourceIdentifierNormalizer = (params) => `${params.typename}:${params.id}`; | ||
const nextTick = (_b = (_a = (typeof process === "object" && typeof process.nextTick === "function" | ||
? process.nextTick | ||
: undefined)) !== null && _a !== void 0 ? _a : setImmediate) !== null && _b !== void 0 ? _b : setTimeout; | ||
class InMemoryLiveQueryStore { | ||
@@ -244,3 +222,2 @@ constructor(params) { | ||
this._idFieldName = "id"; | ||
this._indices = null; | ||
this.makeExecute = (execute) => (args) => { | ||
@@ -291,14 +268,22 @@ const { schema: inputSchema, document, rootValue, contextValue, variableValues, operationName, ...additionalArguments } = args; | ||
})); | ||
const { asyncIterableIterator: iterator, pushValue } = pushPullAsyncIterableIterator.makePushPullAsyncIterableIterator(); | ||
// keep track that current execution is the latest in order to prevent race-conditions :) | ||
let executionCounter = 0; | ||
let previousIdentifier = new Set(rootFieldIdentifier); | ||
const record = { | ||
iterator, | ||
pushValue, | ||
run: () => { | ||
const context = this; | ||
return new repeater.Repeater(async function liveQueryRepeater(push, onStop) { | ||
// utils for throttle | ||
let cancelThrottle; | ||
let run; | ||
let executionCounter = 0; | ||
let previousIdentifier = new Set(rootFieldIdentifier); | ||
function scheduleRun() { | ||
run(); | ||
} | ||
function dispose() { | ||
cancelThrottle === null || cancelThrottle === void 0 ? void 0 : cancelThrottle(); | ||
context._resourceTracker.release(scheduleRun, previousIdentifier); | ||
} | ||
onStop.then(dispose); | ||
run = function run() { | ||
executionCounter = executionCounter + 1; | ||
const counter = executionCounter; | ||
const newIdentifier = new Set(rootFieldIdentifier); | ||
const collectResourceIdentifier = (parameter) => newIdentifier.add(this._buildResourceIdentifier(parameter)); | ||
const collectResourceIdentifier = (parameter) => newIdentifier.add(context._buildResourceIdentifier(parameter)); | ||
const addResourceIdentifier = (values) => { | ||
@@ -316,8 +301,2 @@ if (isNone(values)) { | ||
}; | ||
const context = { | ||
[ORIGINAL_CONTEXT_SYMBOL]: contextValue, | ||
collectResourceIdentifier, | ||
addResourceIdentifier, | ||
indices: this._indices, | ||
}; | ||
const result = execute({ | ||
@@ -328,3 +307,7 @@ schema, | ||
rootValue, | ||
contextValue: context, | ||
contextValue: { | ||
[ORIGINAL_CONTEXT_SYMBOL]: contextValue, | ||
collectResourceIdentifier, | ||
addResourceIdentifier, | ||
}, | ||
variableValues, | ||
@@ -334,30 +317,13 @@ ...additionalArguments, | ||
}); | ||
// result cannot be a AsyncIterableIterator if the `NoLiveMixedWithDeferStreamRule` was used. | ||
// in case anyone forgot to add it we just panic and stop the execution :) | ||
const handleAsyncIterator = (iterator) => { | ||
var _a; | ||
(_a = iterator.return) === null || _a === void 0 ? void 0 : _a.call(iterator); | ||
record.pushValue({ | ||
errors: [ | ||
new graphql.GraphQLError(`"execute" returned a AsyncIterator instead of a MaybePromise<ExecutionResult>. The "NoLiveMixedWithDeferStreamRule" rule might have been skipped.`), | ||
], | ||
}); | ||
// delay to next tick to ensure the error is delivered to listeners. | ||
// TODO: figure out whether there is a better way for doing this. | ||
nextTick(() => { | ||
record.iterator.return(); | ||
}); | ||
this._resourceTracker.release(record, previousIdentifier); | ||
}; | ||
runWith(result, (result) => { | ||
if (pushPullAsyncIterableIterator.isAsyncIterable(result)) { | ||
handleAsyncIterator(result); | ||
if (utils.isAsyncIterable(result)) { | ||
onStop(new Error(`"execute" returned a AsyncIterator instead of a MaybePromise<ExecutionResult>. The "NoLiveMixedWithDeferStreamRule" rule might have been skipped.`)); | ||
return; | ||
} | ||
if (counter === executionCounter) { | ||
this._resourceTracker.track(record, previousIdentifier, newIdentifier); | ||
context._resourceTracker.track(scheduleRun, previousIdentifier, newIdentifier); | ||
previousIdentifier = newIdentifier; | ||
const liveResult = result; | ||
liveResult.isLive = true; | ||
if (this._includeIdentifierExtension === true) { | ||
if (context._includeIdentifierExtension === true) { | ||
if (!liveResult.extensions) { | ||
@@ -369,25 +335,14 @@ liveResult.extensions = {}; | ||
} | ||
record.pushValue(liveResult); | ||
push(liveResult); | ||
} | ||
}); | ||
}, | ||
}; | ||
// utils for throttle | ||
let cancelThrottle; | ||
if (isSome(throttleValue)) { | ||
const { run, cancel } = throttle(record.run, throttleValue); | ||
record.run = run; | ||
cancelThrottle = cancel; | ||
} | ||
this._resourceTracker.register(record, previousIdentifier); | ||
// Execute initial query | ||
record.run(); | ||
// TODO: figure out how we can do this stuff without monkey-patching the iterator | ||
const originalReturn = iterator.return.bind(iterator); | ||
iterator.return = () => { | ||
cancelThrottle === null || cancelThrottle === void 0 ? void 0 : cancelThrottle(); | ||
this._resourceTracker.release(record, previousIdentifier); | ||
return originalReturn(); | ||
}; | ||
return iterator; | ||
}; | ||
if (isSome(throttleValue)) { | ||
const throttled = throttle(run, throttleValue); | ||
run = throttled.run; | ||
cancelThrottle = throttled.cancel; | ||
} | ||
context._resourceTracker.register(scheduleRun, previousIdentifier); | ||
scheduleRun(); | ||
}); | ||
}; | ||
@@ -408,13 +363,2 @@ /** @deprecated Please use InMemoryLiveQueryStore.makeExecute instead. */ | ||
} | ||
if (params === null || params === void 0 ? void 0 : params.indexBy) { | ||
this._indices = new Map(); | ||
for (const { field, args } of params.indexBy) { | ||
let indices = this._indices.get(field); | ||
if (!indices) { | ||
indices = []; | ||
this._indices.set(field, indices); | ||
} | ||
indices.push(args); | ||
} | ||
} | ||
this._includeIdentifierExtension = | ||
@@ -446,4 +390,4 @@ (_a = params === null || params === void 0 ? void 0 : params.includeIdentifierExtension) !== null && _a !== void 0 ? _a : (typeof process === "undefined" | ||
const records = this._resourceTracker.getRecordsForIdentifiers(identifiers); | ||
for (const record of records) { | ||
record.run(); | ||
for (const run of records) { | ||
run(); | ||
} | ||
@@ -450,0 +394,0 @@ } |
@@ -5,5 +5,2 @@ import { ExecutionResult, execute as defaultExecute, ExecutionArgs } from "graphql"; | ||
declare type PromiseOrValue<T> = T | Promise<T>; | ||
declare type ArgumentName = string; | ||
declare type ArgumentValue = string; | ||
declare type IndexConfiguration = Array<ArgumentName | [arg: ArgumentName, value: ArgumentValue]>; | ||
export declare type BuildResourceIdentifierFunction = (parameter: Readonly<{ | ||
@@ -49,9 +46,2 @@ typename: string; | ||
validateThrottleValue?: ValidateThrottleValueFunction; | ||
/** | ||
* Specify which fields should be indexed for specific invalidations. | ||
*/ | ||
indexBy?: Array<{ | ||
field: string; | ||
args: IndexConfiguration; | ||
}>; | ||
}; | ||
@@ -67,3 +57,2 @@ declare type LiveExecuteReturnType = PromiseOrValue<AsyncIterableIterator<ExecutionResult | LiveExecutionResult> | ExecutionResult>; | ||
private _validateThrottleValue; | ||
private _indices; | ||
constructor(params?: InMemoryLiveQueryStoreParameter); | ||
@@ -70,0 +59,0 @@ private _getPatchedSchema; |
{ | ||
"name": "@n1ru4l/in-memory-live-query-store", | ||
"version": "0.9.0-alpha-51b566b.0", | ||
"version": "0.9.0-alpha-e02cfcd.0", | ||
"peerDependencies": { | ||
@@ -10,3 +10,3 @@ "graphql": "^15.4.0 || ^16.0.0" | ||
"@n1ru4l/graphql-live-query": "0.9.0", | ||
"@n1ru4l/push-pull-async-iterable-iterator": "^3.0.0" | ||
"@repeaterjs/repeater": "^3.0.4" | ||
}, | ||
@@ -13,0 +13,0 @@ "repository": { |
46199
878
+ Added@repeaterjs/repeater@^3.0.4
+ Added@repeaterjs/repeater@3.0.6(transitive)
- Removed@n1ru4l/push-pull-async-iterable-iterator@3.2.0(transitive)