@isograph/react 0.0.0-main-5da1ab92 → 0.0.0-main-6bd3135f

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.
@@ -5,6 +5,7 @@ import { FragmentReference, Variables } from './FragmentReference';
5
5
  import { PromiseWrapper } from './PromiseWrapper';
6
6
  import { IsographEntrypoint } from './entrypoint';
7
7
  import type { ReaderAst } from './reader';
8
+ import { LogFunction, WrappedLogFunction } from './logging';
8
9
  export type ComponentOrFieldName = string;
9
10
  export type StringifiedArgs = string;
10
11
  type ComponentCache = {
@@ -53,6 +54,7 @@ export type IsographEnvironment = {
53
54
  readonly retainedQueries: Set<RetainedQuery>;
54
55
  readonly gcBuffer: Array<RetainedQuery>;
55
56
  readonly gcBufferSize: number;
57
+ readonly loggers: Set<WrappedLogFunction>;
56
58
  };
57
59
  export type MissingFieldHandler = (storeRecord: StoreRecord, root: DataId, fieldName: string, arguments_: {
58
60
  [index: string]: any;
@@ -73,7 +75,7 @@ export type IsographStore = {
73
75
  [index: DataId]: StoreRecord | null;
74
76
  readonly __ROOT: StoreRecord;
75
77
  };
76
- export declare function createIsographEnvironment(store: IsographStore, networkFunction: IsographNetworkFunction, missingFieldHandler?: MissingFieldHandler): IsographEnvironment;
78
+ export declare function createIsographEnvironment(store: IsographStore, networkFunction: IsographNetworkFunction, missingFieldHandler?: MissingFieldHandler | null, logFunction?: LogFunction | null): IsographEnvironment;
77
79
  export declare function createIsographStore(): IsographStore;
78
80
  export declare function defaultMissingFieldHandler(_storeRecord: StoreRecord, _root: DataId, fieldName: string, arguments_: {
79
81
  [index: string]: any;
@@ -10,7 +10,7 @@ exports.getOrLoadIsographArtifact = getOrLoadIsographArtifact;
10
10
  const PromiseWrapper_1 = require("./PromiseWrapper");
11
11
  exports.ROOT_ID = '__ROOT';
12
12
  const DEFAULT_GC_BUFFER_SIZE = 10;
13
- function createIsographEnvironment(store, networkFunction, missingFieldHandler) {
13
+ function createIsographEnvironment(store, networkFunction, missingFieldHandler, logFunction) {
14
14
  return {
15
15
  store,
16
16
  networkFunction,
@@ -22,6 +22,7 @@ function createIsographEnvironment(store, networkFunction, missingFieldHandler)
22
22
  retainedQueries: new Set(),
23
23
  gcBuffer: [],
24
24
  gcBufferSize: DEFAULT_GC_BUFFER_SIZE,
25
+ loggers: logFunction != null ? new Set([{ log: logFunction }]) : new Set(),
25
26
  };
26
27
  }
27
28
  function createIsographStore() {
@@ -20,7 +20,7 @@ export declare function getOrCreateCacheForArtifact<TReadFromStore extends {
20
20
  }, TClientFieldValue>(environment: IsographEnvironment, entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>, variables: ExtractParameters<TReadFromStore>): ParentCache<FragmentReference<TReadFromStore, TClientFieldValue>>;
21
21
  type NetworkResponseScalarValue = string | number | boolean;
22
22
  type NetworkResponseValue = NetworkResponseScalarValue | null | NetworkResponseObject | NetworkResponseObject[] | NetworkResponseScalarValue[];
23
- type NetworkResponseObject = {
23
+ export type NetworkResponseObject = {
24
24
  [index: string]: undefined | NetworkResponseValue;
25
25
  id?: DataId;
26
26
  };
@@ -16,16 +16,16 @@ const read_1 = require("./read");
16
16
  const areEqualWithDeepComparison_1 = require("./areEqualWithDeepComparison");
17
17
  const makeNetworkRequest_1 = require("./makeNetworkRequest");
18
18
  const PromiseWrapper_1 = require("./PromiseWrapper");
19
+ const logging_1 = require("./logging");
19
20
  const TYPENAME_FIELD_NAME = '__typename';
20
21
  function getOrCreateItemInSuspenseCache(environment, index, factory) {
21
- // @ts-expect-error
22
- if (typeof window !== 'undefined' && window.__LOG) {
23
- console.log('getting cache for', {
24
- index,
25
- cache: Object.keys(environment.fragmentCache),
26
- found: !!environment.fragmentCache[index],
27
- });
28
- }
22
+ // TODO this is probably a useless message, we should remove it
23
+ (0, logging_1.logMessage)(environment, {
24
+ kind: 'GettingSuspenseCacheItem',
25
+ index,
26
+ availableCacheItems: Object.keys(environment.fragmentCache),
27
+ found: !!environment.fragmentCache[index],
28
+ });
29
29
  if (environment.fragmentCache[index] == null) {
30
30
  environment.fragmentCache[index] = new react_disposable_state_1.ParentCache(factory);
31
31
  }
@@ -76,19 +76,18 @@ function getOrCreateCacheForArtifact(environment, entrypoint, variables) {
76
76
  }
77
77
  function normalizeData(environment, normalizationAst, networkResponse, variables, nestedRefetchQueries) {
78
78
  const encounteredIds = new Set();
79
- // @ts-expect-error
80
- if (typeof window !== 'undefined' && window.__LOG) {
81
- console.log('about to normalize', normalizationAst, networkResponse, variables);
82
- }
79
+ (0, logging_1.logMessage)(environment, {
80
+ kind: 'AboutToNormalize',
81
+ normalizationAst,
82
+ networkResponse,
83
+ variables,
84
+ });
83
85
  normalizeDataIntoRecord(environment, normalizationAst, networkResponse, environment.store.__ROOT, IsographEnvironment_1.ROOT_ID, variables, nestedRefetchQueries, encounteredIds);
84
- // @ts-expect-error
85
- if (typeof window !== 'undefined' && window.__LOG) {
86
- console.log('after normalization', {
87
- store: environment.store,
88
- encounteredIds,
89
- environment,
90
- });
91
- }
86
+ (0, logging_1.logMessage)(environment, {
87
+ kind: 'AfterNormalization',
88
+ store: environment.store,
89
+ encounteredIds,
90
+ });
92
91
  callSubscriptions(environment, encounteredIds);
93
92
  return encounteredIds;
94
93
  }
@@ -168,26 +167,16 @@ function callSubscriptions(environment, recordsEncounteredWhenNormalizing) {
168
167
  throwOnNetworkError: false,
169
168
  });
170
169
  const mergedItem = (0, areEqualWithDeepComparison_1.mergeObjectsUsingReaderAst)(subscription.readerAst, subscription.encounteredDataAndRecords.item, newEncounteredDataAndRecords.item);
170
+ (0, logging_1.logMessage)(environment, {
171
+ kind: 'DeepEqualityCheck',
172
+ fragmentReference: subscription.fragmentReference,
173
+ old: subscription.encounteredDataAndRecords.item,
174
+ new: newEncounteredDataAndRecords.item,
175
+ deeplyEqual: mergedItem === subscription.encounteredDataAndRecords.item,
176
+ });
171
177
  if (mergedItem !== subscription.encounteredDataAndRecords.item) {
172
- // @ts-expect-error
173
- if (typeof window !== 'undefined' && window.__LOG) {
174
- console.log('Deep equality - No', {
175
- fragmentReference: subscription.fragmentReference,
176
- old: subscription.encounteredDataAndRecords.item,
177
- new: newEncounteredDataAndRecords.item,
178
- });
179
- }
180
178
  subscription.callback(newEncounteredDataAndRecords);
181
179
  }
182
- else {
183
- // @ts-expect-error
184
- if (typeof window !== 'undefined' && window.__LOG) {
185
- console.log('Deep equality - Yes', {
186
- fragmentReference: subscription.fragmentReference,
187
- old: subscription.encounteredDataAndRecords.item,
188
- });
189
- }
190
- }
191
180
  }
192
181
  return;
193
182
  }
@@ -4,6 +4,7 @@ exports.getOrCreateCachedComponent = getOrCreateCachedComponent;
4
4
  const cache_1 = require("./cache");
5
5
  const useReadAndSubscribe_1 = require("../react/useReadAndSubscribe");
6
6
  const PromiseWrapper_1 = require("./PromiseWrapper");
7
+ const logging_1 = require("./logging");
7
8
  function getOrCreateCachedComponent(environment, componentName, fragmentReference, networkRequestOptions) {
8
9
  var _a, _b, _c;
9
10
  // cachedComponentsById is a three layer cache: id, then component name, then
@@ -21,13 +22,11 @@ function getOrCreateCachedComponent(environment, componentName, fragmentReferenc
21
22
  function Component(additionalRuntimeProps) {
22
23
  const readerWithRefetchQueries = (0, PromiseWrapper_1.readPromise)(fragmentReference.readerWithRefetchQueries);
23
24
  const data = (0, useReadAndSubscribe_1.useReadAndSubscribe)(fragmentReference, networkRequestOptions, readerWithRefetchQueries.readerArtifact.readerAst);
24
- // @ts-expect-error
25
- if (typeof window !== 'undefined' && window.__LOG) {
26
- console.log('Component re-rendered: ' +
27
- componentName +
28
- ' ' +
29
- fragmentReference.root);
30
- }
25
+ (0, logging_1.logMessage)(environment, {
26
+ kind: 'ComponentRerendered',
27
+ componentName,
28
+ rootId: fragmentReference.root,
29
+ });
31
30
  const firstParameter = {
32
31
  data,
33
32
  parameters: fragmentReference.variables,
@@ -0,0 +1,60 @@
1
+ import { CleanupFn } from '@isograph/disposable-types';
2
+ import { DataId, IsographEnvironment, IsographStore, StoreRecord } from './IsographEnvironment';
3
+ import { IsographEntrypoint, NormalizationAst, RefetchQueryNormalizationArtifact } from './entrypoint';
4
+ import { FragmentReference, Variables } from './FragmentReference';
5
+ import { NetworkResponseObject } from './cache';
6
+ import { Arguments } from './util';
7
+ import { ReadDataResult } from './read';
8
+ export type LogMessage = {
9
+ kind: 'GettingSuspenseCacheItem';
10
+ index: string;
11
+ availableCacheItems: ReadonlyArray<string>;
12
+ found: boolean;
13
+ } | {
14
+ kind: 'AboutToNormalize';
15
+ normalizationAst: NormalizationAst;
16
+ networkResponse: NetworkResponseObject;
17
+ variables: Variables;
18
+ } | {
19
+ kind: 'AfterNormalization';
20
+ store: IsographStore;
21
+ encounteredIds: Set<string>;
22
+ } | {
23
+ kind: 'DeepEqualityCheck';
24
+ fragmentReference: FragmentReference<any, any>;
25
+ old: object;
26
+ new: object;
27
+ deeplyEqual: boolean;
28
+ } | {
29
+ kind: 'ComponentRerendered';
30
+ componentName: string;
31
+ rootId: DataId;
32
+ } | {
33
+ kind: 'MakeNetworkRequest';
34
+ artifact: RefetchQueryNormalizationArtifact | IsographEntrypoint<any, any>;
35
+ variables: Variables;
36
+ networkRequestId: string;
37
+ } | {
38
+ kind: 'ReceivedNetworkResponse';
39
+ networkResponse: any;
40
+ networkRequestId: string;
41
+ } | {
42
+ kind: 'MissingFieldHandlerCalled';
43
+ root: DataId;
44
+ storeRecord: StoreRecord;
45
+ fieldName: string;
46
+ arguments: Arguments | null;
47
+ variables: Variables;
48
+ } | {
49
+ kind: 'DoneReading';
50
+ response: ReadDataResult<any>;
51
+ } | {
52
+ kind: 'NonEntrypointReceived';
53
+ entrypoint: any;
54
+ };
55
+ export type LogFunction = (logMessage: LogMessage) => void;
56
+ export type WrappedLogFunction = {
57
+ log: LogFunction;
58
+ };
59
+ export declare function logMessage(environment: IsographEnvironment, message: LogMessage): void;
60
+ export declare function registerLogger(environment: IsographEnvironment, log: LogFunction): CleanupFn;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logMessage = logMessage;
4
+ exports.registerLogger = registerLogger;
5
+ function logMessage(environment, message) {
6
+ for (const logger of environment.loggers) {
7
+ try {
8
+ logger.log(message);
9
+ }
10
+ catch (_a) { }
11
+ }
12
+ }
13
+ function registerLogger(environment, log) {
14
+ const wrapped = { log };
15
+ environment.loggers.add(wrapped);
16
+ return () => {
17
+ environment.loggers.delete(wrapped);
18
+ };
19
+ }
@@ -4,11 +4,18 @@ exports.makeNetworkRequest = makeNetworkRequest;
4
4
  const garbageCollection_1 = require("./garbageCollection");
5
5
  const PromiseWrapper_1 = require("./PromiseWrapper");
6
6
  const cache_1 = require("./cache");
7
+ const logging_1 = require("./logging");
8
+ let networkRequestId = 0;
7
9
  function makeNetworkRequest(environment, artifact, variables) {
8
- // @ts-expect-error
9
- if (typeof window !== 'undefined' && window.__LOG) {
10
- console.log('make network request', artifact, variables);
11
- }
10
+ // TODO this should be a DataId and stored in the store
11
+ const myNetworkRequestId = networkRequestId + '';
12
+ networkRequestId++;
13
+ (0, logging_1.logMessage)(environment, {
14
+ kind: 'MakeNetworkRequest',
15
+ artifact,
16
+ variables,
17
+ networkRequestId: myNetworkRequestId,
18
+ });
12
19
  let status = {
13
20
  kind: 'UndisposedIncomplete',
14
21
  };
@@ -17,10 +24,11 @@ function makeNetworkRequest(environment, artifact, variables) {
17
24
  .networkFunction(artifact.queryText, variables)
18
25
  .then((networkResponse) => {
19
26
  var _a;
20
- // @ts-expect-error
21
- if (typeof window !== 'undefined' && window.__LOG) {
22
- console.log('network response', artifact, networkResponse);
23
- }
27
+ (0, logging_1.logMessage)(environment, {
28
+ kind: 'ReceivedNetworkResponse',
29
+ networkResponse,
30
+ networkRequestId: myNetworkRequestId,
31
+ });
24
32
  if (networkResponse.errors != null) {
25
33
  // @ts-expect-error Why are we getting the wrong constructor here?
26
34
  throw new Error('GraphQL network response had errors', {
@@ -8,6 +8,16 @@ export declare function readButDoNotEvaluate<TReadFromStore extends {
8
8
  parameters: object;
9
9
  data: object;
10
10
  }>(environment: IsographEnvironment, fragmentReference: FragmentReference<TReadFromStore, unknown>, networkRequestOptions: NetworkRequestReaderOptions): WithEncounteredRecords<TReadFromStore>;
11
+ export type ReadDataResult<TReadFromStore> = {
12
+ readonly kind: 'Success';
13
+ readonly data: ExtractData<TReadFromStore>;
14
+ readonly encounteredRecords: Set<DataId>;
15
+ } | {
16
+ readonly kind: 'MissingData';
17
+ readonly reason: string;
18
+ readonly nestedReason?: ReadDataResult<unknown>;
19
+ readonly recordId: DataId;
20
+ };
11
21
  export type NetworkRequestReaderOptions = {
12
22
  suspendIfInFlight: boolean;
13
23
  throwOnNetworkError: boolean;
package/dist/core/read.js CHANGED
@@ -7,15 +7,16 @@ const componentCache_1 = require("./componentCache");
7
7
  const IsographEnvironment_1 = require("./IsographEnvironment");
8
8
  const makeNetworkRequest_1 = require("./makeNetworkRequest");
9
9
  const PromiseWrapper_1 = require("./PromiseWrapper");
10
+ const logging_1 = require("./logging");
10
11
  function readButDoNotEvaluate(environment, fragmentReference, networkRequestOptions) {
11
12
  var _a;
12
13
  const mutableEncounteredRecords = new Set();
13
14
  const readerWithRefetchQueries = (0, PromiseWrapper_1.readPromise)(fragmentReference.readerWithRefetchQueries);
14
15
  const response = readData(environment, readerWithRefetchQueries.readerArtifact.readerAst, fragmentReference.root, (_a = fragmentReference.variables) !== null && _a !== void 0 ? _a : {}, readerWithRefetchQueries.nestedRefetchQueries, fragmentReference.networkRequest, networkRequestOptions, mutableEncounteredRecords);
15
- // @ts-expect-error
16
- if (typeof window !== 'undefined' && window.__LOG) {
17
- console.log('done reading: ' + response.kind, { response });
18
- }
16
+ (0, logging_1.logMessage)(environment, {
17
+ kind: 'DoneReading',
18
+ response,
19
+ });
19
20
  if (response.kind === 'MissingData') {
20
21
  // There are two cases here that we care about:
21
22
  // 1. the network request is in flight, we haven't suspend on it, and we want
@@ -126,6 +127,14 @@ function readData(environment, ast, root, variables, nestedRefetchQueries, netwo
126
127
  // TODO make this configurable, and also generated and derived from the schema
127
128
  const missingFieldHandler = (_c = environment.missingFieldHandler) !== null && _c !== void 0 ? _c : IsographEnvironment_1.defaultMissingFieldHandler;
128
129
  const altLink = missingFieldHandler(storeRecord, root, field.fieldName, field.arguments, variables);
130
+ (0, logging_1.logMessage)(environment, {
131
+ kind: 'MissingFieldHandlerCalled',
132
+ root,
133
+ storeRecord,
134
+ fieldName: field.fieldName,
135
+ arguments: field.arguments,
136
+ variables,
137
+ });
129
138
  if (altLink === undefined) {
130
139
  return {
131
140
  kind: 'MissingData',
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { type NormalizationAst, type NormalizationAstNode, type NormalizationLin
8
8
  export { readButDoNotEvaluate } from './core/read';
9
9
  export { type ExtractSecondParam, type Argument, type ArgumentName, type ArgumentValue, type Arguments, } from './core/util';
10
10
  export { type FragmentReference, type Variables, type ExtractParameters, type ExtractData, stableIdForFragmentReference, } from './core/FragmentReference';
11
+ export { type LogMessage, type LogFunction, logMessage, registerLogger, } from './core/logging';
11
12
  export { IsographEnvironmentProvider, useIsographEnvironment, type IsographEnvironmentProviderProps, } from './react/IsographEnvironmentProvider';
12
13
  export { useImperativeReference } from './react/useImperativeReference';
13
14
  export { FragmentReader } from './react/FragmentReader';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useImperativeLoadableField = exports.useConnectionSpecPagination = exports.useSkipLimitPagination = exports.useImperativeExposedMutationField = exports.useClientSideDefer = exports.RenderAfterCommit__DO_NOT_USE = exports.useRerenderOnChange = exports.useLazyReference = exports.useSubscribeToMultiple = exports.useReadAndSubscribe = exports.useResult = exports.FragmentReader = exports.useImperativeReference = exports.useIsographEnvironment = exports.IsographEnvironmentProvider = exports.stableIdForFragmentReference = exports.readButDoNotEvaluate = exports.assertIsEntrypoint = exports.defaultMissingFieldHandler = exports.createIsographStore = exports.createIsographEnvironment = exports.ROOT_ID = exports.makeNetworkRequest = exports.normalizeData = exports.subscribe = exports.wrapPromise = exports.wrapResolvedValue = exports.getPromiseState = exports.readPromise = exports.garbageCollectEnvironment = exports.unretainQuery = exports.retainQuery = void 0;
3
+ exports.useImperativeLoadableField = exports.useConnectionSpecPagination = exports.useSkipLimitPagination = exports.useImperativeExposedMutationField = exports.useClientSideDefer = exports.RenderAfterCommit__DO_NOT_USE = exports.useRerenderOnChange = exports.useLazyReference = exports.useSubscribeToMultiple = exports.useReadAndSubscribe = exports.useResult = exports.FragmentReader = exports.useImperativeReference = exports.useIsographEnvironment = exports.IsographEnvironmentProvider = exports.registerLogger = exports.logMessage = exports.stableIdForFragmentReference = exports.readButDoNotEvaluate = exports.assertIsEntrypoint = exports.defaultMissingFieldHandler = exports.createIsographStore = exports.createIsographEnvironment = exports.ROOT_ID = exports.makeNetworkRequest = exports.normalizeData = exports.subscribe = exports.wrapPromise = exports.wrapResolvedValue = exports.getPromiseState = exports.readPromise = exports.garbageCollectEnvironment = exports.unretainQuery = exports.retainQuery = void 0;
4
4
  var garbageCollection_1 = require("./core/garbageCollection");
5
5
  Object.defineProperty(exports, "retainQuery", { enumerable: true, get: function () { return garbageCollection_1.retainQuery; } });
6
6
  Object.defineProperty(exports, "unretainQuery", { enumerable: true, get: function () { return garbageCollection_1.unretainQuery; } });
@@ -26,6 +26,9 @@ var read_1 = require("./core/read");
26
26
  Object.defineProperty(exports, "readButDoNotEvaluate", { enumerable: true, get: function () { return read_1.readButDoNotEvaluate; } });
27
27
  var FragmentReference_1 = require("./core/FragmentReference");
28
28
  Object.defineProperty(exports, "stableIdForFragmentReference", { enumerable: true, get: function () { return FragmentReference_1.stableIdForFragmentReference; } });
29
+ var logging_1 = require("./core/logging");
30
+ Object.defineProperty(exports, "logMessage", { enumerable: true, get: function () { return logging_1.logMessage; } });
31
+ Object.defineProperty(exports, "registerLogger", { enumerable: true, get: function () { return logging_1.registerLogger; } });
29
32
  var IsographEnvironmentProvider_1 = require("./react/IsographEnvironmentProvider");
30
33
  Object.defineProperty(exports, "IsographEnvironmentProvider", { enumerable: true, get: function () { return IsographEnvironmentProvider_1.IsographEnvironmentProvider; } });
31
34
  Object.defineProperty(exports, "useIsographEnvironment", { enumerable: true, get: function () { return IsographEnvironmentProvider_1.useIsographEnvironment; } });
@@ -4,15 +4,16 @@ exports.useLazyReference = useLazyReference;
4
4
  const IsographEnvironmentProvider_1 = require("./IsographEnvironmentProvider");
5
5
  const cache_1 = require("../core/cache");
6
6
  const react_disposable_state_1 = require("@isograph/react-disposable-state");
7
+ const logging_1 = require("../core/logging");
7
8
  function useLazyReference(entrypoint, variables) {
8
- // @ts-expect-error
9
- if (typeof window !== 'undefined' && window.__LOG) {
10
- if ((entrypoint === null || entrypoint === void 0 ? void 0 : entrypoint.kind) !== 'Entrypoint') {
11
- console.warn('useLazyReference was passed an unexpected or invalid object as the first parameter. ' +
12
- 'Is the babel plugin correctly configured? Received=', entrypoint);
13
- }
14
- }
15
9
  const environment = (0, IsographEnvironmentProvider_1.useIsographEnvironment)();
10
+ if ((entrypoint === null || entrypoint === void 0 ? void 0 : entrypoint.kind) !== 'Entrypoint') {
11
+ // TODO have a separate error logger
12
+ (0, logging_1.logMessage)(environment, {
13
+ kind: 'NonEntrypointReceived',
14
+ entrypoint,
15
+ });
16
+ }
16
17
  const cache = (0, cache_1.getOrCreateCacheForArtifact)(environment, entrypoint, variables);
17
18
  return {
18
19
  fragmentReference: (0, react_disposable_state_1.useLazyDisposableState)(cache).state,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isograph/react",
3
- "version": "0.0.0-main-5da1ab92",
3
+ "version": "0.0.0-main-6bd3135f",
4
4
  "description": "Use Isograph with React",
5
5
  "homepage": "https://isograph.dev",
6
6
  "main": "dist/index.js",
@@ -17,9 +17,9 @@
17
17
  "tsc": "tsc"
18
18
  },
19
19
  "dependencies": {
20
- "@isograph/disposable-types": "0.0.0-main-5da1ab92",
21
- "@isograph/react-disposable-state": "0.0.0-main-5da1ab92",
22
- "@isograph/reference-counted-pointer": "0.0.0-main-5da1ab92"
20
+ "@isograph/disposable-types": "0.0.0-main-6bd3135f",
21
+ "@isograph/react-disposable-state": "0.0.0-main-6bd3135f",
22
+ "@isograph/reference-counted-pointer": "0.0.0-main-6bd3135f"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "react": "18.3.1"
@@ -5,6 +5,7 @@ import { FragmentReference, Variables } from './FragmentReference';
5
5
  import { PromiseWrapper, wrapPromise } from './PromiseWrapper';
6
6
  import { IsographEntrypoint } from './entrypoint';
7
7
  import type { ReaderAst } from './reader';
8
+ import { LogFunction, WrappedLogFunction } from './logging';
8
9
 
9
10
  export type ComponentOrFieldName = string;
10
11
  export type StringifiedArgs = string;
@@ -63,6 +64,7 @@ export type IsographEnvironment = {
63
64
  readonly retainedQueries: Set<RetainedQuery>;
64
65
  readonly gcBuffer: Array<RetainedQuery>;
65
66
  readonly gcBufferSize: number;
67
+ readonly loggers: Set<WrappedLogFunction>;
66
68
  };
67
69
 
68
70
  export type MissingFieldHandler = (
@@ -117,7 +119,8 @@ const DEFAULT_GC_BUFFER_SIZE = 10;
117
119
  export function createIsographEnvironment(
118
120
  store: IsographStore,
119
121
  networkFunction: IsographNetworkFunction,
120
- missingFieldHandler?: MissingFieldHandler,
122
+ missingFieldHandler?: MissingFieldHandler | null,
123
+ logFunction?: LogFunction | null,
121
124
  ): IsographEnvironment {
122
125
  return {
123
126
  store,
@@ -130,6 +133,7 @@ export function createIsographEnvironment(
130
133
  retainedQueries: new Set(),
131
134
  gcBuffer: [],
132
135
  gcBufferSize: DEFAULT_GC_BUFFER_SIZE,
136
+ loggers: logFunction != null ? new Set([{ log: logFunction }]) : new Set(),
133
137
  };
134
138
  }
135
139
 
package/src/core/cache.ts CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  import { mergeObjectsUsingReaderAst } from './areEqualWithDeepComparison';
33
33
  import { makeNetworkRequest } from './makeNetworkRequest';
34
34
  import { wrapResolvedValue } from './PromiseWrapper';
35
+ import { logMessage } from './logging';
35
36
 
36
37
  const TYPENAME_FIELD_NAME = '__typename';
37
38
 
@@ -43,14 +44,13 @@ export function getOrCreateItemInSuspenseCache<
43
44
  index: string,
44
45
  factory: Factory<FragmentReference<TReadFromStore, TClientFieldValue>>,
45
46
  ): ParentCache<FragmentReference<TReadFromStore, TClientFieldValue>> {
46
- // @ts-expect-error
47
- if (typeof window !== 'undefined' && window.__LOG) {
48
- console.log('getting cache for', {
49
- index,
50
- cache: Object.keys(environment.fragmentCache),
51
- found: !!environment.fragmentCache[index],
52
- });
53
- }
47
+ // TODO this is probably a useless message, we should remove it
48
+ logMessage(environment, {
49
+ kind: 'GettingSuspenseCacheItem',
50
+ index,
51
+ availableCacheItems: Object.keys(environment.fragmentCache),
52
+ found: !!environment.fragmentCache[index],
53
+ });
54
54
  if (environment.fragmentCache[index] == null) {
55
55
  environment.fragmentCache[index] = new ParentCache(factory);
56
56
  }
@@ -124,7 +124,8 @@ type NetworkResponseValue =
124
124
  | NetworkResponseObject
125
125
  | NetworkResponseObject[]
126
126
  | NetworkResponseScalarValue[];
127
- type NetworkResponseObject = {
127
+
128
+ export type NetworkResponseObject = {
128
129
  // N.B. undefined is here to support optional id's, but
129
130
  // undefined should not *actually* be present in the network response.
130
131
  [index: string]: undefined | NetworkResponseValue;
@@ -140,15 +141,12 @@ export function normalizeData(
140
141
  ): Set<DataId> {
141
142
  const encounteredIds = new Set<DataId>();
142
143
 
143
- // @ts-expect-error
144
- if (typeof window !== 'undefined' && window.__LOG) {
145
- console.log(
146
- 'about to normalize',
147
- normalizationAst,
148
- networkResponse,
149
- variables,
150
- );
151
- }
144
+ logMessage(environment, {
145
+ kind: 'AboutToNormalize',
146
+ normalizationAst,
147
+ networkResponse,
148
+ variables,
149
+ });
152
150
  normalizeDataIntoRecord(
153
151
  environment,
154
152
  normalizationAst,
@@ -159,14 +157,13 @@ export function normalizeData(
159
157
  nestedRefetchQueries,
160
158
  encounteredIds,
161
159
  );
162
- // @ts-expect-error
163
- if (typeof window !== 'undefined' && window.__LOG) {
164
- console.log('after normalization', {
165
- store: environment.store,
166
- encounteredIds,
167
- environment,
168
- });
169
- }
160
+
161
+ logMessage(environment, {
162
+ kind: 'AfterNormalization',
163
+ store: environment.store,
164
+ encounteredIds,
165
+ });
166
+
170
167
  callSubscriptions(environment, encounteredIds);
171
168
  return encounteredIds;
172
169
  }
@@ -292,24 +289,18 @@ function callSubscriptions(
292
289
  subscription.encounteredDataAndRecords.item,
293
290
  newEncounteredDataAndRecords.item,
294
291
  );
292
+
293
+ logMessage(environment, {
294
+ kind: 'DeepEqualityCheck',
295
+ fragmentReference: subscription.fragmentReference,
296
+ old: subscription.encounteredDataAndRecords.item,
297
+ new: newEncounteredDataAndRecords.item,
298
+ deeplyEqual:
299
+ mergedItem === subscription.encounteredDataAndRecords.item,
300
+ });
301
+
295
302
  if (mergedItem !== subscription.encounteredDataAndRecords.item) {
296
- // @ts-expect-error
297
- if (typeof window !== 'undefined' && window.__LOG) {
298
- console.log('Deep equality - No', {
299
- fragmentReference: subscription.fragmentReference,
300
- old: subscription.encounteredDataAndRecords.item,
301
- new: newEncounteredDataAndRecords.item,
302
- });
303
- }
304
303
  subscription.callback(newEncounteredDataAndRecords);
305
- } else {
306
- // @ts-expect-error
307
- if (typeof window !== 'undefined' && window.__LOG) {
308
- console.log('Deep equality - Yes', {
309
- fragmentReference: subscription.fragmentReference,
310
- old: subscription.encounteredDataAndRecords.item,
311
- });
312
- }
313
304
  }
314
305
  }
315
306
  return;
@@ -4,6 +4,7 @@ import { FragmentReference } from './FragmentReference';
4
4
  import { useReadAndSubscribe } from '../react/useReadAndSubscribe';
5
5
  import { NetworkRequestReaderOptions } from './read';
6
6
  import { readPromise } from './PromiseWrapper';
7
+ import { logMessage } from './logging';
7
8
 
8
9
  export function getOrCreateCachedComponent(
9
10
  environment: IsographEnvironment,
@@ -40,15 +41,11 @@ export function getOrCreateCachedComponent(
40
41
  readerWithRefetchQueries.readerArtifact.readerAst,
41
42
  );
42
43
 
43
- // @ts-expect-error
44
- if (typeof window !== 'undefined' && window.__LOG) {
45
- console.log(
46
- 'Component re-rendered: ' +
47
- componentName +
48
- ' ' +
49
- fragmentReference.root,
50
- );
51
- }
44
+ logMessage(environment, {
45
+ kind: 'ComponentRerendered',
46
+ componentName,
47
+ rootId: fragmentReference.root,
48
+ });
52
49
 
53
50
  const firstParameter = {
54
51
  data,
@@ -0,0 +1,106 @@
1
+ import { CleanupFn } from '@isograph/disposable-types';
2
+ import {
3
+ DataId,
4
+ IsographEnvironment,
5
+ IsographStore,
6
+ StoreRecord,
7
+ } from './IsographEnvironment';
8
+ import {
9
+ IsographEntrypoint,
10
+ NormalizationAst,
11
+ RefetchQueryNormalizationArtifact,
12
+ } from './entrypoint';
13
+ import { FragmentReference, Variables } from './FragmentReference';
14
+ import { NetworkResponseObject } from './cache';
15
+ import { Arguments } from './util';
16
+ import { ReadDataResult } from './read';
17
+
18
+ export type LogMessage =
19
+ | {
20
+ kind: 'GettingSuspenseCacheItem';
21
+ index: string;
22
+ availableCacheItems: ReadonlyArray<string>;
23
+ found: boolean;
24
+ }
25
+ | {
26
+ kind: 'AboutToNormalize';
27
+ normalizationAst: NormalizationAst;
28
+ networkResponse: NetworkResponseObject;
29
+ variables: Variables;
30
+ }
31
+ | {
32
+ kind: 'AfterNormalization';
33
+ store: IsographStore;
34
+ encounteredIds: Set<string>;
35
+ }
36
+ | {
37
+ kind: 'DeepEqualityCheck';
38
+ fragmentReference: FragmentReference<any, any>;
39
+ old: object;
40
+ new: object;
41
+ deeplyEqual: boolean;
42
+ }
43
+ | {
44
+ kind: 'ComponentRerendered';
45
+ componentName: string;
46
+ rootId: DataId;
47
+ }
48
+ | {
49
+ kind: 'MakeNetworkRequest';
50
+ artifact:
51
+ | RefetchQueryNormalizationArtifact
52
+ | IsographEntrypoint<any, any>;
53
+ variables: Variables;
54
+ networkRequestId: string;
55
+ }
56
+ | {
57
+ kind: 'ReceivedNetworkResponse';
58
+ // TODO should be object
59
+ networkResponse: any;
60
+ networkRequestId: string;
61
+ }
62
+ | {
63
+ kind: 'MissingFieldHandlerCalled';
64
+ root: DataId;
65
+ storeRecord: StoreRecord;
66
+ fieldName: string;
67
+ arguments: Arguments | null;
68
+ variables: Variables;
69
+ }
70
+ | {
71
+ kind: 'DoneReading';
72
+ response: ReadDataResult<any>;
73
+ }
74
+ | {
75
+ kind: 'NonEntrypointReceived';
76
+ entrypoint: any;
77
+ };
78
+
79
+ export type LogFunction = (logMessage: LogMessage) => void;
80
+
81
+ // wrapped so that items in the loggers set are unique.
82
+ export type WrappedLogFunction = {
83
+ log: LogFunction;
84
+ };
85
+
86
+ export function logMessage(
87
+ environment: IsographEnvironment,
88
+ message: LogMessage,
89
+ ) {
90
+ for (const logger of environment.loggers) {
91
+ try {
92
+ logger.log(message);
93
+ } catch {}
94
+ }
95
+ }
96
+
97
+ export function registerLogger(
98
+ environment: IsographEnvironment,
99
+ log: LogFunction,
100
+ ): CleanupFn {
101
+ const wrapped = { log };
102
+ environment.loggers.add(wrapped);
103
+ return () => {
104
+ environment.loggers.delete(wrapped);
105
+ };
106
+ }
@@ -13,16 +13,26 @@ import {
13
13
  import { IsographEnvironment } from './IsographEnvironment';
14
14
  import { AnyError, PromiseWrapper, wrapPromise } from './PromiseWrapper';
15
15
  import { normalizeData } from './cache';
16
+ import { logMessage } from './logging';
17
+
18
+ let networkRequestId = 0;
16
19
 
17
20
  export function makeNetworkRequest(
18
21
  environment: IsographEnvironment,
19
22
  artifact: RefetchQueryNormalizationArtifact | IsographEntrypoint<any, any>,
20
23
  variables: Variables,
21
24
  ): ItemCleanupPair<PromiseWrapper<void, AnyError>> {
22
- // @ts-expect-error
23
- if (typeof window !== 'undefined' && window.__LOG) {
24
- console.log('make network request', artifact, variables);
25
- }
25
+ // TODO this should be a DataId and stored in the store
26
+ const myNetworkRequestId = networkRequestId + '';
27
+ networkRequestId++;
28
+
29
+ logMessage(environment, {
30
+ kind: 'MakeNetworkRequest',
31
+ artifact,
32
+ variables,
33
+ networkRequestId: myNetworkRequestId,
34
+ });
35
+
26
36
  let status: NetworkRequestStatus = {
27
37
  kind: 'UndisposedIncomplete',
28
38
  };
@@ -30,10 +40,11 @@ export function makeNetworkRequest(
30
40
  const promise = environment
31
41
  .networkFunction(artifact.queryText, variables)
32
42
  .then((networkResponse) => {
33
- // @ts-expect-error
34
- if (typeof window !== 'undefined' && window.__LOG) {
35
- console.log('network response', artifact, networkResponse);
36
- }
43
+ logMessage(environment, {
44
+ kind: 'ReceivedNetworkResponse',
45
+ networkResponse,
46
+ networkRequestId: myNetworkRequestId,
47
+ });
37
48
 
38
49
  if (networkResponse.errors != null) {
39
50
  // @ts-expect-error Why are we getting the wrong constructor here?
package/src/core/read.ts CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  } from './PromiseWrapper';
29
29
  import { ReaderAst } from './reader';
30
30
  import { Arguments } from './util';
31
+ import { logMessage } from './logging';
31
32
 
32
33
  export type WithEncounteredRecords<T> = {
33
34
  readonly encounteredRecords: Set<DataId>;
@@ -57,10 +58,12 @@ export function readButDoNotEvaluate<
57
58
  networkRequestOptions,
58
59
  mutableEncounteredRecords,
59
60
  );
60
- // @ts-expect-error
61
- if (typeof window !== 'undefined' && window.__LOG) {
62
- console.log('done reading: ' + response.kind, { response });
63
- }
61
+
62
+ logMessage(environment, {
63
+ kind: 'DoneReading',
64
+ response,
65
+ });
66
+
64
67
  if (response.kind === 'MissingData') {
65
68
  // There are two cases here that we care about:
66
69
  // 1. the network request is in flight, we haven't suspend on it, and we want
@@ -90,7 +93,7 @@ export function readButDoNotEvaluate<
90
93
  }
91
94
  }
92
95
 
93
- type ReadDataResult<TReadFromStore> =
96
+ export type ReadDataResult<TReadFromStore> =
94
97
  | {
95
98
  readonly kind: 'Success';
96
99
  readonly data: ExtractData<TReadFromStore>;
@@ -207,6 +210,7 @@ function readData<TReadFromStore>(
207
210
  // TODO make this configurable, and also generated and derived from the schema
208
211
  const missingFieldHandler =
209
212
  environment.missingFieldHandler ?? defaultMissingFieldHandler;
213
+
210
214
  const altLink = missingFieldHandler(
211
215
  storeRecord,
212
216
  root,
@@ -214,6 +218,15 @@ function readData<TReadFromStore>(
214
218
  field.arguments,
215
219
  variables,
216
220
  );
221
+ logMessage(environment, {
222
+ kind: 'MissingFieldHandlerCalled',
223
+ root,
224
+ storeRecord,
225
+ fieldName: field.fieldName,
226
+ arguments: field.arguments,
227
+ variables,
228
+ });
229
+
217
230
  if (altLink === undefined) {
218
231
  return {
219
232
  kind: 'MissingData',
package/src/index.ts CHANGED
@@ -67,6 +67,12 @@ export {
67
67
  type ExtractData,
68
68
  stableIdForFragmentReference,
69
69
  } from './core/FragmentReference';
70
+ export {
71
+ type LogMessage,
72
+ type LogFunction,
73
+ logMessage,
74
+ registerLogger,
75
+ } from './core/logging';
70
76
 
71
77
  export {
72
78
  IsographEnvironmentProvider,
@@ -6,6 +6,7 @@ import { useIsographEnvironment } from './IsographEnvironmentProvider';
6
6
  import { IsographEntrypoint } from '../core/entrypoint';
7
7
  import { getOrCreateCacheForArtifact } from '../core/cache';
8
8
  import { useLazyDisposableState } from '@isograph/react-disposable-state';
9
+ import { logMessage } from '../core/logging';
9
10
 
10
11
  export function useLazyReference<
11
12
  TReadFromStore extends { parameters: object; data: object },
@@ -16,18 +17,16 @@ export function useLazyReference<
16
17
  ): {
17
18
  fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>;
18
19
  } {
19
- // @ts-expect-error
20
- if (typeof window !== 'undefined' && window.__LOG) {
21
- if (entrypoint?.kind !== 'Entrypoint') {
22
- console.warn(
23
- 'useLazyReference was passed an unexpected or invalid object as the first parameter. ' +
24
- 'Is the babel plugin correctly configured? Received=',
25
- entrypoint,
26
- );
27
- }
20
+ const environment = useIsographEnvironment();
21
+
22
+ if (entrypoint?.kind !== 'Entrypoint') {
23
+ // TODO have a separate error logger
24
+ logMessage(environment, {
25
+ kind: 'NonEntrypointReceived',
26
+ entrypoint,
27
+ });
28
28
  }
29
29
 
30
- const environment = useIsographEnvironment();
31
30
  const cache = getOrCreateCacheForArtifact(environment, entrypoint, variables);
32
31
 
33
32
  return {