@fluidframework/react 2.70.0-361788 → 2.71.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @fluidframework/react
2
2
 
3
+ ## 2.71.0
4
+
5
+ Dependency updates only.
6
+
7
+ ## 2.70.0
8
+
9
+ Dependency updates only.
10
+
3
11
  ## 2.63.0
4
12
 
5
13
  Dependency updates only.
@@ -1 +1 @@
1
- {"version":3,"file":"propNode.js","sourceRoot":"","sources":["../src/propNode.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AA6EH;;;;;;GAMG;AACH,SAAgB,kBAAkB,CACjC,QAA8B;IAE9B,OAAO,QAAa,CAAC;AACtB,CAAC;AAJD,gDAIC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CACnC,KAAQ;IAER,OAAO,KAAoC,CAAC;AAC7C,CAAC;AAJD,oDAIC;AAgDD;;;GAGG;AACH,SAAgB,cAAc,CAAqC,IAAO;IACzE,OAAO,IAAmC,CAAC;AAC5C,CAAC;AAFD,wCAEC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAuB,IAAO;IAC7D,OAAO,IAA4C,CAAC;AACrD,CAAC;AAFD,4CAEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ErasedType } from \"@fluidframework/core-interfaces\";\nimport type { TreeNode, TreeLeafValue } from \"@fluidframework/tree\";\n\n/**\n * A type erased TreeNode for use in react props.\n * @remarks\n * Read content from the node using {@link usePropTreeNode} or {@link usePropTreeRecord}.\n *\n * In events where tracking dependencies is not required, the node can be unwrapped using {@link unwrapPropTreeNode}.\n *\n * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, \"PropTreeNode\"]> {}\n\n/**\n * Type TreeNodes in T as {@link PropTreeNode}s.\n * @remarks\n * This only handles a few cases (TreeNode, structurally typed objects fields and arrays) and leaves other types as is.\n * Users which provide other types (e.g. maps) which contain TreeNodes will need to handle wrapping those themselves if the wrapping is desired.\n *\n * Users of this should not rely on a given use of TreeNode not being wrapped:\n * future changes to this API may add more cases which are wrapped, and this will be considered a non-breaking change.\n * @privateRemarks\n * Covering all cases is impossible, and trying to cover more with recursive mapped types can break some of the types by losing methods, private members, etc.\n * To mitigate this IsMappableObjectType is used for objects, and only mappable types, where the mapping actually impacted the type are modified.\n *\n * This is intended to cover the common cases, and users can handle other cases manually.\n * See the tests for this for more details.\n * @alpha\n */\nexport type WrapNodes<T> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T extends readonly (infer U)[]\n\t\t? readonly WrapNodes<U>[]\n\t\t: // `T extends (infer U)` distributes over unions, allowing WrapNodes<A|B> to be WrapNodes<A> | WrapNodes<B>.\n\t\t\tT extends infer U\n\t\t\t? IsMappableObjectType<\n\t\t\t\t\tU,\n\t\t\t\t\t{\n\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t} extends U\n\t\t\t\t\t\t? // Returning U in this case (when assignable to the mapped type) avoids flatting named interfaces when they are unchanged.\n\t\t\t\t\t\t\tU\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t\t\t},\n\t\t\t\t\tT\n\t\t\t\t>\n\t\t\t: T;\n\n/**\n * Detect if a type is a simple structural object.\n * @remarks\n * This returns the true case if the type is entirely defined by its set of public properties.\n * More concretely, this indicates if creating a mapped type based on `T`\n * will be lossy due to details mapped types cannot access.\n *\n * This is shallow, and distributes over unions.\n *\n * This also returns the true case for primitive types since mapping over them leaves them unchanged if doing so in a generic context:\n * Mapping over a primitive does not leave them unchanged if done directly (not to a generic type parameter), but this can not detect that behavior.\n * This is fine as the use for this is to detect when making a mapped type from a generic type parameter would be lossy.\n * @system @alpha\n */\nexport type IsMappableObjectType<\n\tT,\n\tTrue = true,\n\tFalse = false,\n\tMapped = {\n\t\t[P in keyof T]: T[P];\n\t},\n> = [Mapped] extends [T] ? ([T] extends [Mapped] ? True : False) : False;\n\n/**\n * Casts a node from a {@link PropTreeNode} back to a TreeNode.\n * @remarks\n * This should only be done in scenarios where tracking observations is not required (such as event handlers),\n * or when taking care to handle invalidation manually.\n * @alpha\n */\nexport function unwrapPropTreeNode<T extends TreeNode | TreeLeafValue>(\n\tpropNode: PropTreeValue<T> | T,\n): T {\n\treturn propNode as T;\n}\n\n/**\n * {@link unwrapPropTreeNode} but for a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function unwrapPropTreeRecord<T extends PropTreeNodeRecord>(\n\tprops: T,\n): UnwrapPropTreeNodeRecord<T> {\n\treturn props as UnwrapPropTreeNodeRecord<T>;\n}\n\n/**\n * {@inheritdoc unwrapPropTreeNode}\n * @alpha\n */\nexport type UnwrapPropTreeNode<T extends TreeLeafValue | PropTreeNode<TreeNode> | undefined> =\n\tT extends PropTreeNode<infer Node> ? Node : T;\n\n/**\n * Record that can contain TreeNodes.\n * @alpha\n */\nexport type NodeRecord = Record<string, TreeNode | TreeLeafValue>;\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type WrapPropTreeNodeRecord<T extends NodeRecord> = {\n\treadonly [P in keyof T]: PropTreeValue<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type UnwrapPropTreeNodeRecord<T extends PropTreeNodeRecord> = {\n\treadonly [P in keyof T]: UnwrapPropTreeNode<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeNodeRecord = Record<\n\tstring,\n\tTreeLeafValue | PropTreeNode<TreeNode> | undefined\n>;\n\n/**\n * Type erase a `TreeNode` from a `TreeNode | TreeLeafValue` as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeValue<T extends TreeNode | TreeLeafValue | undefined> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T;\n\n/**\n * Type erase a TreeNode as a {@link PropTreeNode}.\n * @alpha\n */\nexport function toPropTreeNode<T extends TreeNode | TreeLeafValue>(node: T): PropTreeValue<T> {\n\treturn node as unknown as PropTreeValue<T>;\n}\n\n/**\n * Type erase a {@link NodeRecord} as a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function toPropTreeRecord<T extends NodeRecord>(node: T): WrapPropTreeNodeRecord<T> {\n\treturn node as unknown as WrapPropTreeNodeRecord<T>;\n}\n"]}
1
+ {"version":3,"file":"propNode.js","sourceRoot":"","sources":["../src/propNode.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AA6EH;;;;;;GAMG;AACH,SAAgB,kBAAkB,CACjC,QAA8B;IAE9B,OAAO,QAAa,CAAC;AACtB,CAAC;AAJD,gDAIC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CACnC,KAAQ;IAER,OAAO,KAAoC,CAAC;AAC7C,CAAC;AAJD,oDAIC;AAgDD;;;GAGG;AACH,SAAgB,cAAc,CAAqC,IAAO;IACzE,OAAO,IAAmC,CAAC;AAC5C,CAAC;AAFD,wCAEC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAuB,IAAO;IAC7D,OAAO,IAA4C,CAAC;AACrD,CAAC;AAFD,4CAEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ErasedType } from \"@fluidframework/core-interfaces\";\nimport type { TreeNode, TreeLeafValue } from \"@fluidframework/tree\";\n\n/**\n * A type erased TreeNode for use in react props.\n * @remarks\n * Read content from the node using {@link usePropTreeNode} or {@link usePropTreeRecord}.\n *\n * In events where tracking dependencies is not required, the node can be unwrapped using {@link unwrapPropTreeNode}.\n *\n * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, \"PropTreeNode\"]> {}\n\n/**\n * Type TreeNodes in T as {@link PropTreeNode}s.\n * @remarks\n * This only handles a few cases (TreeNode, structurally typed objects fields and arrays) and leaves other types as is.\n * Users which provide other types (e.g. maps) which contain TreeNodes will need to handle wrapping those themselves if the wrapping is desired.\n *\n * Users of this should not rely on a given use of TreeNode not being wrapped:\n * future changes to this API may add more cases which are wrapped, and this will be considered a non-breaking change.\n * @privateRemarks\n * Covering all cases is impossible, and trying to cover more with recursive mapped types can break some of the types by losing methods, private members, etc.\n * To mitigate this IsMappableObjectType is used for objects, and only mappable types, where the mapping actually impacted the type are modified.\n *\n * This is intended to cover the common cases, and users can handle other cases manually.\n * See the tests for this for more details.\n * @alpha\n */\nexport type WrapNodes<T> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T extends readonly (infer U)[]\n\t\t? readonly WrapNodes<U>[]\n\t\t: // `T extends (infer U)` distributes over unions, allowing WrapNodes<A|B> to be WrapNodes<A> | WrapNodes<B>.\n\t\t\tT extends infer U\n\t\t\t? IsMappableObjectType<\n\t\t\t\t\tU,\n\t\t\t\t\t{\n\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t} extends U\n\t\t\t\t\t\t? // Returning U in this case (when assignable to the mapped type) avoids flatting named interfaces when they are unchanged.\n\t\t\t\t\t\t\tU\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t\t\t},\n\t\t\t\t\tT\n\t\t\t\t>\n\t\t\t: T;\n\n/**\n * Detect if a type is a simple structural object.\n * @remarks\n * This returns the true case if the type is entirely defined by its set of public properties.\n * More concretely, this indicates if creating a mapped type based on `T`\n * will be lossy due to details mapped types cannot access.\n *\n * This is shallow, and distributes over unions.\n *\n * This also returns the true case for primitive types since mapping over them leaves them unchanged if doing so in a generic context:\n * Mapping over a primitive does not leave them unchanged if done directly (not to a generic type parameter), but this can not detect that behavior.\n * This is fine as the use for this is to detect when making a mapped type from a generic type parameter would be lossy.\n * @system @alpha\n */\nexport type IsMappableObjectType<\n\tT,\n\tTrue = true,\n\tFalse = false,\n\tMapped = {\n\t\t[P in keyof T]: T[P];\n\t},\n> = [Mapped] extends [T] ? ([T] extends [Mapped] ? True : False) : False;\n\n/**\n * Casts a node from a {@link PropTreeNode} back to a TreeNode.\n * @remarks\n * This should only be done in scenarios where tracking observations is not required (such as event handlers),\n * or when taking care to handle invalidation manually.\n * @alpha\n */\nexport function unwrapPropTreeNode<T extends TreeNode | TreeLeafValue>(\n\tpropNode: PropTreeValue<T> | T,\n): T {\n\treturn propNode as T;\n}\n\n/**\n * {@link unwrapPropTreeNode} but for a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function unwrapPropTreeRecord<T extends PropTreeNodeRecord>(\n\tprops: T,\n): UnwrapPropTreeNodeRecord<T> {\n\treturn props as UnwrapPropTreeNodeRecord<T>;\n}\n\n/**\n * {@inheritdoc unwrapPropTreeNode}\n * @alpha\n */\nexport type UnwrapPropTreeNode<T extends TreeLeafValue | PropTreeNode<TreeNode> | undefined> =\n\tT extends PropTreeNode<infer Node> ? Node : T;\n\n/**\n * Record that can contain TreeNodes.\n * @alpha\n */\nexport type NodeRecord = Record<string, TreeNode | TreeLeafValue>;\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type WrapPropTreeNodeRecord<T extends NodeRecord> = {\n\treadonly [P in keyof T]: PropTreeValue<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type UnwrapPropTreeNodeRecord<T extends PropTreeNodeRecord> = {\n\treadonly [P in keyof T]: UnwrapPropTreeNode<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeNodeRecord = Record<\n\tstring,\n\tTreeLeafValue | PropTreeNode<TreeNode> | undefined\n>;\n\n/**\n * Type erase a `TreeNode` from a `TreeNode | TreeLeafValue` as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeValue<T extends TreeNode | TreeLeafValue | undefined> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T;\n\n/**\n * Type erase a TreeNode as a {@link PropTreeNode}.\n * @alpha\n */\nexport function toPropTreeNode<T extends TreeNode | TreeLeafValue>(node: T): PropTreeValue<T> {\n\treturn node as unknown as PropTreeValue<T>;\n}\n\n/**\n * Type erase a {@link NodeRecord} as a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function toPropTreeRecord<T extends NodeRecord>(node: T): WrapPropTreeNodeRecord<T> {\n\treturn node as unknown as WrapPropTreeNodeRecord<T>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"propNode.js","sourceRoot":"","sources":["../src/propNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6EH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CACjC,QAA8B;IAE9B,OAAO,QAAa,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,KAAQ;IAER,OAAO,KAAoC,CAAC;AAC7C,CAAC;AAgDD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAqC,IAAO;IACzE,OAAO,IAAmC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAuB,IAAO;IAC7D,OAAO,IAA4C,CAAC;AACrD,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ErasedType } from \"@fluidframework/core-interfaces\";\nimport type { TreeNode, TreeLeafValue } from \"@fluidframework/tree\";\n\n/**\n * A type erased TreeNode for use in react props.\n * @remarks\n * Read content from the node using {@link usePropTreeNode} or {@link usePropTreeRecord}.\n *\n * In events where tracking dependencies is not required, the node can be unwrapped using {@link unwrapPropTreeNode}.\n *\n * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, \"PropTreeNode\"]> {}\n\n/**\n * Type TreeNodes in T as {@link PropTreeNode}s.\n * @remarks\n * This only handles a few cases (TreeNode, structurally typed objects fields and arrays) and leaves other types as is.\n * Users which provide other types (e.g. maps) which contain TreeNodes will need to handle wrapping those themselves if the wrapping is desired.\n *\n * Users of this should not rely on a given use of TreeNode not being wrapped:\n * future changes to this API may add more cases which are wrapped, and this will be considered a non-breaking change.\n * @privateRemarks\n * Covering all cases is impossible, and trying to cover more with recursive mapped types can break some of the types by losing methods, private members, etc.\n * To mitigate this IsMappableObjectType is used for objects, and only mappable types, where the mapping actually impacted the type are modified.\n *\n * This is intended to cover the common cases, and users can handle other cases manually.\n * See the tests for this for more details.\n * @alpha\n */\nexport type WrapNodes<T> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T extends readonly (infer U)[]\n\t\t? readonly WrapNodes<U>[]\n\t\t: // `T extends (infer U)` distributes over unions, allowing WrapNodes<A|B> to be WrapNodes<A> | WrapNodes<B>.\n\t\t\tT extends infer U\n\t\t\t? IsMappableObjectType<\n\t\t\t\t\tU,\n\t\t\t\t\t{\n\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t} extends U\n\t\t\t\t\t\t? // Returning U in this case (when assignable to the mapped type) avoids flatting named interfaces when they are unchanged.\n\t\t\t\t\t\t\tU\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t\t\t},\n\t\t\t\t\tT\n\t\t\t\t>\n\t\t\t: T;\n\n/**\n * Detect if a type is a simple structural object.\n * @remarks\n * This returns the true case if the type is entirely defined by its set of public properties.\n * More concretely, this indicates if creating a mapped type based on `T`\n * will be lossy due to details mapped types cannot access.\n *\n * This is shallow, and distributes over unions.\n *\n * This also returns the true case for primitive types since mapping over them leaves them unchanged if doing so in a generic context:\n * Mapping over a primitive does not leave them unchanged if done directly (not to a generic type parameter), but this can not detect that behavior.\n * This is fine as the use for this is to detect when making a mapped type from a generic type parameter would be lossy.\n * @system @alpha\n */\nexport type IsMappableObjectType<\n\tT,\n\tTrue = true,\n\tFalse = false,\n\tMapped = {\n\t\t[P in keyof T]: T[P];\n\t},\n> = [Mapped] extends [T] ? ([T] extends [Mapped] ? True : False) : False;\n\n/**\n * Casts a node from a {@link PropTreeNode} back to a TreeNode.\n * @remarks\n * This should only be done in scenarios where tracking observations is not required (such as event handlers),\n * or when taking care to handle invalidation manually.\n * @alpha\n */\nexport function unwrapPropTreeNode<T extends TreeNode | TreeLeafValue>(\n\tpropNode: PropTreeValue<T> | T,\n): T {\n\treturn propNode as T;\n}\n\n/**\n * {@link unwrapPropTreeNode} but for a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function unwrapPropTreeRecord<T extends PropTreeNodeRecord>(\n\tprops: T,\n): UnwrapPropTreeNodeRecord<T> {\n\treturn props as UnwrapPropTreeNodeRecord<T>;\n}\n\n/**\n * {@inheritdoc unwrapPropTreeNode}\n * @alpha\n */\nexport type UnwrapPropTreeNode<T extends TreeLeafValue | PropTreeNode<TreeNode> | undefined> =\n\tT extends PropTreeNode<infer Node> ? Node : T;\n\n/**\n * Record that can contain TreeNodes.\n * @alpha\n */\nexport type NodeRecord = Record<string, TreeNode | TreeLeafValue>;\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type WrapPropTreeNodeRecord<T extends NodeRecord> = {\n\treadonly [P in keyof T]: PropTreeValue<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type UnwrapPropTreeNodeRecord<T extends PropTreeNodeRecord> = {\n\treadonly [P in keyof T]: UnwrapPropTreeNode<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeNodeRecord = Record<\n\tstring,\n\tTreeLeafValue | PropTreeNode<TreeNode> | undefined\n>;\n\n/**\n * Type erase a `TreeNode` from a `TreeNode | TreeLeafValue` as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeValue<T extends TreeNode | TreeLeafValue | undefined> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T;\n\n/**\n * Type erase a TreeNode as a {@link PropTreeNode}.\n * @alpha\n */\nexport function toPropTreeNode<T extends TreeNode | TreeLeafValue>(node: T): PropTreeValue<T> {\n\treturn node as unknown as PropTreeValue<T>;\n}\n\n/**\n * Type erase a {@link NodeRecord} as a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function toPropTreeRecord<T extends NodeRecord>(node: T): WrapPropTreeNodeRecord<T> {\n\treturn node as unknown as WrapPropTreeNodeRecord<T>;\n}\n"]}
1
+ {"version":3,"file":"propNode.js","sourceRoot":"","sources":["../src/propNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6EH;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CACjC,QAA8B;IAE9B,OAAO,QAAa,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,KAAQ;IAER,OAAO,KAAoC,CAAC;AAC7C,CAAC;AAgDD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAqC,IAAO;IACzE,OAAO,IAAmC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAuB,IAAO;IAC7D,OAAO,IAA4C,CAAC;AACrD,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ErasedType } from \"@fluidframework/core-interfaces\";\nimport type { TreeNode, TreeLeafValue } from \"@fluidframework/tree\";\n\n/**\n * A type erased TreeNode for use in react props.\n * @remarks\n * Read content from the node using {@link usePropTreeNode} or {@link usePropTreeRecord}.\n *\n * In events where tracking dependencies is not required, the node can be unwrapped using {@link unwrapPropTreeNode}.\n *\n * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.\n * @alpha\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, \"PropTreeNode\"]> {}\n\n/**\n * Type TreeNodes in T as {@link PropTreeNode}s.\n * @remarks\n * This only handles a few cases (TreeNode, structurally typed objects fields and arrays) and leaves other types as is.\n * Users which provide other types (e.g. maps) which contain TreeNodes will need to handle wrapping those themselves if the wrapping is desired.\n *\n * Users of this should not rely on a given use of TreeNode not being wrapped:\n * future changes to this API may add more cases which are wrapped, and this will be considered a non-breaking change.\n * @privateRemarks\n * Covering all cases is impossible, and trying to cover more with recursive mapped types can break some of the types by losing methods, private members, etc.\n * To mitigate this IsMappableObjectType is used for objects, and only mappable types, where the mapping actually impacted the type are modified.\n *\n * This is intended to cover the common cases, and users can handle other cases manually.\n * See the tests for this for more details.\n * @alpha\n */\nexport type WrapNodes<T> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T extends readonly (infer U)[]\n\t\t? readonly WrapNodes<U>[]\n\t\t: // `T extends (infer U)` distributes over unions, allowing WrapNodes<A|B> to be WrapNodes<A> | WrapNodes<B>.\n\t\t\tT extends infer U\n\t\t\t? IsMappableObjectType<\n\t\t\t\t\tU,\n\t\t\t\t\t{\n\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t} extends U\n\t\t\t\t\t\t? // Returning U in this case (when assignable to the mapped type) avoids flatting named interfaces when they are unchanged.\n\t\t\t\t\t\t\tU\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t[P in keyof U]: WrapNodes<U[P]>;\n\t\t\t\t\t\t\t},\n\t\t\t\t\tT\n\t\t\t\t>\n\t\t\t: T;\n\n/**\n * Detect if a type is a simple structural object.\n * @remarks\n * This returns the true case if the type is entirely defined by its set of public properties.\n * More concretely, this indicates if creating a mapped type based on `T`\n * will be lossy due to details mapped types cannot access.\n *\n * This is shallow, and distributes over unions.\n *\n * This also returns the true case for primitive types since mapping over them leaves them unchanged if doing so in a generic context:\n * Mapping over a primitive does not leave them unchanged if done directly (not to a generic type parameter), but this can not detect that behavior.\n * This is fine as the use for this is to detect when making a mapped type from a generic type parameter would be lossy.\n * @system @alpha\n */\nexport type IsMappableObjectType<\n\tT,\n\tTrue = true,\n\tFalse = false,\n\tMapped = {\n\t\t[P in keyof T]: T[P];\n\t},\n> = [Mapped] extends [T] ? ([T] extends [Mapped] ? True : False) : False;\n\n/**\n * Casts a node from a {@link PropTreeNode} back to a TreeNode.\n * @remarks\n * This should only be done in scenarios where tracking observations is not required (such as event handlers),\n * or when taking care to handle invalidation manually.\n * @alpha\n */\nexport function unwrapPropTreeNode<T extends TreeNode | TreeLeafValue>(\n\tpropNode: PropTreeValue<T> | T,\n): T {\n\treturn propNode as T;\n}\n\n/**\n * {@link unwrapPropTreeNode} but for a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function unwrapPropTreeRecord<T extends PropTreeNodeRecord>(\n\tprops: T,\n): UnwrapPropTreeNodeRecord<T> {\n\treturn props as UnwrapPropTreeNodeRecord<T>;\n}\n\n/**\n * {@inheritdoc unwrapPropTreeNode}\n * @alpha\n */\nexport type UnwrapPropTreeNode<T extends TreeLeafValue | PropTreeNode<TreeNode> | undefined> =\n\tT extends PropTreeNode<infer Node> ? Node : T;\n\n/**\n * Record that can contain TreeNodes.\n * @alpha\n */\nexport type NodeRecord = Record<string, TreeNode | TreeLeafValue>;\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type WrapPropTreeNodeRecord<T extends NodeRecord> = {\n\treadonly [P in keyof T]: PropTreeValue<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type UnwrapPropTreeNodeRecord<T extends PropTreeNodeRecord> = {\n\treadonly [P in keyof T]: UnwrapPropTreeNode<T[P]>;\n};\n\n/**\n * Type erase `TreeNode`s from a {@link NodeRecord} as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeNodeRecord = Record<\n\tstring,\n\tTreeLeafValue | PropTreeNode<TreeNode> | undefined\n>;\n\n/**\n * Type erase a `TreeNode` from a `TreeNode | TreeLeafValue` as a {@link PropTreeNode}.\n * @alpha\n */\nexport type PropTreeValue<T extends TreeNode | TreeLeafValue | undefined> = T extends TreeNode\n\t? PropTreeNode<T>\n\t: T;\n\n/**\n * Type erase a TreeNode as a {@link PropTreeNode}.\n * @alpha\n */\nexport function toPropTreeNode<T extends TreeNode | TreeLeafValue>(node: T): PropTreeValue<T> {\n\treturn node as unknown as PropTreeValue<T>;\n}\n\n/**\n * Type erase a {@link NodeRecord} as a {@link PropTreeNodeRecord}.\n * @alpha\n */\nexport function toPropTreeRecord<T extends NodeRecord>(node: T): WrapPropTreeNodeRecord<T> {\n\treturn node as unknown as WrapPropTreeNodeRecord<T>;\n}\n"]}
@@ -23,7 +23,7 @@ describe("useObservation", () => {
23
23
  useObservationWithEffects,
24
24
  ];
25
25
  for (const useObservationHook of observationHooks) {
26
- // eslint-disable-next-line @typescript-eslint/ban-types
26
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type, @typescript-eslint/ban-types
27
27
  describe(useObservationHook.name, () => {
28
28
  for (const reactStrictMode of [false, true]) {
29
29
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"useObservation.spec.js","sourceRoot":"","sources":["../../src/test/useObservation.spec.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport * as React from \"react\";\n\nimport {\n\tuseObservation,\n\tuseObservationStrict,\n\tuseObservationWithEffects,\n} from \"../useObservation.js\";\n\n// There is much more coverage of useObservation via useTree tests.\n\ndescribe(\"useObservation\", () => {\n\tdescribe(\"dom tests\", () => {\n\t\tlet cleanup: () => void;\n\n\t\tbefore(() => {\n\t\t\tcleanup = globalJsdom();\n\t\t});\n\n\t\tafter(() => {\n\t\t\tcleanup();\n\t\t});\n\n\t\tconst observationHooks: (typeof useObservation)[] = [\n\t\t\tuseObservation,\n\t\t\tuseObservationStrict,\n\t\t\tuseObservationWithEffects,\n\t\t] as const;\n\n\t\tfor (const useObservationHook of observationHooks) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/ban-types\n\t\t\tdescribe((useObservationHook as Function).name, () => {\n\t\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Check then clear the contents of `log`.\n\t\t\t\t\t *\n\t\t\t\t\t * @remarks When in StrictMode, React may double render, so that case is not checked for an exact match.\n\t\t\t\t\t */\n\t\t\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\t\t\tfunction checkRenderLog(log: string[], expected: readonly string[]): void {\n\t\t\t\t\t\tif (reactStrictMode) {\n\t\t\t\t\t\t\tassert.deepEqual(new Set(log), new Set(expected));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.deepEqual(log, expected);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.length = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\t\tit(\"useObservation\", async () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tlog.push(`useObservation`);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: (\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// In real usage, this would unsubscribe from any events.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlog.push(\"click\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidate\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst content = <TestComponent />;\n\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\", \"useObservation\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\n\t\t\t\t\t\t\tconst button =\n\t\t\t\t\t\t\t\trendered.baseElement.querySelector(\"button\") ??\n\t\t\t\t\t\t\t\tassert.fail(\"button not found\");\n\t\t\t\t\t\t\tbutton.click();\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"click\", \"invalidated\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\n\t\t\t\t\t\t\t\t\"render\",\n\t\t\t\t\t\t\t\t...(reactStrictMode && useObservationHook !== useObservationWithEffects\n\t\t\t\t\t\t\t\t\t? [\"unsubscribe\"]\n\t\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\t\"useObservation\",\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// This requires waiting for finalizers.\n\t\t\t\t\t\t// Forcing two async GCs seems to work robustly, so this is enabled, but if it becomes flakey, it can be tweaked and/or skipped.\n\t\t\t\t\t\tit(`unsubscribe on unmount`, async () => {\n\t\t\t\t\t\t\tassert(global.gc);\n\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(this: unknown): JSX.Element {\n\t\t\t\t\t\t\t\treturn useObservationHook((invalidate) => ({\n\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\t// Unsubscribe on unmount is done via FinalizationRegistry, so force a GC and wait for it.\n\t\t\t\t\t\t\t// For this to pass on NodeJs experimentally is has been found that this must either do:\n\t\t\t\t\t\t\t// 1. a sync GC then a wait of 8 seconds (but this sometimes fails after multiple runs unless a debugger takes a heap snapshot, possible due to some JIT optimization that breaks it).\n\t\t\t\t\t\t\t// 2. two async GCs in a row.\n\t\t\t\t\t\t\t// Since the second option is both more robust and faster, that is what is used here.\n\t\t\t\t\t\t\tfor (let index = 0; index < 2; index++) {\n\t\t\t\t\t\t\t\tawait global.gc({ type: \"major\", execution: \"async\" });\n\t\t\t\t\t\t\t\tif (log.length > 0) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"unsubscribe\"]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidate after unmount\", () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tlet logUnsubscribe = true;\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tif (logUnsubscribe) log.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tconst invalidateCallbacks: (() => void)[] = [];\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tinvalidateCallbacks.push(invalidate);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\"]);\n\n\t\t\t\t\t\t\t// After unmount, unsubscribe could happen at any time due to finalizer,so suppress logging it to prevent the test from possibly becoming flaky.\n\t\t\t\t\t\t\tlogUnsubscribe = false;\n\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\tassert(invalidateCallbacks.length === (reactStrictMode ? 2 : 1));\n\n\t\t\t\t\t\t\t// Invalidate after unmount.\n\t\t\t\t\t\t\t// Since this can happen in real use, due to unsubscribe delay while waiting for finalizer, ensure it does not cause issues.\n\t\t\t\t\t\t\t// This should be a no-op, but since it does a React SetState after unmount, React could object to it.\n\t\t\t\t\t\t\tfor (const callback of invalidateCallbacks) {\n\t\t\t\t\t\t\t\tcallback();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Confirm the invalidation happened..\n\t\t\t\t\t\t\t// If we didn't suppress unsubscribe logging, and the finalizer had run, this could fail (which is why we suppress it).\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"invalidated\"]);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n});\n\n/**\n * Assert that an array is empty.\n *\n * @remarks\n * Not inlined because doing so causes TypeScript to infer the array type as never[] afterwards and breaks push.\n * Better than asserting length is 0 as this gets a better error message on failure.\n */\nfunction assertLogEmpty(log: string[]): void {\n\tassert.deepEqual(log, []);\n}\n"]}
1
+ {"version":3,"file":"useObservation.spec.js","sourceRoot":"","sources":["../../src/test/useObservation.spec.tsx"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { strict as assert } from \"node:assert\";\n\nimport { render } from \"@testing-library/react\";\nimport globalJsdom from \"global-jsdom\";\nimport * as React from \"react\";\n\nimport {\n\tuseObservation,\n\tuseObservationStrict,\n\tuseObservationWithEffects,\n} from \"../useObservation.js\";\n\n// There is much more coverage of useObservation via useTree tests.\n\ndescribe(\"useObservation\", () => {\n\tdescribe(\"dom tests\", () => {\n\t\tlet cleanup: () => void;\n\n\t\tbefore(() => {\n\t\t\tcleanup = globalJsdom();\n\t\t});\n\n\t\tafter(() => {\n\t\t\tcleanup();\n\t\t});\n\n\t\tconst observationHooks: (typeof useObservation)[] = [\n\t\t\tuseObservation,\n\t\t\tuseObservationStrict,\n\t\t\tuseObservationWithEffects,\n\t\t] as const;\n\n\t\tfor (const useObservationHook of observationHooks) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type, @typescript-eslint/ban-types\n\t\t\tdescribe((useObservationHook as Function).name, () => {\n\t\t\t\tfor (const reactStrictMode of [false, true]) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Check then clear the contents of `log`.\n\t\t\t\t\t *\n\t\t\t\t\t * @remarks When in StrictMode, React may double render, so that case is not checked for an exact match.\n\t\t\t\t\t */\n\t\t\t\t\t// eslint-disable-next-line no-inner-declarations\n\t\t\t\t\tfunction checkRenderLog(log: string[], expected: readonly string[]): void {\n\t\t\t\t\t\tif (reactStrictMode) {\n\t\t\t\t\t\t\tassert.deepEqual(new Set(log), new Set(expected));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tassert.deepEqual(log, expected);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.length = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tdescribe(`StrictMode: ${reactStrictMode}`, () => {\n\t\t\t\t\t\tit(\"useObservation\", async () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tlog.push(`useObservation`);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: (\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// In real usage, this would unsubscribe from any events.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlog.push(\"click\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidate\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst content = <TestComponent />;\n\n\t\t\t\t\t\t\tconst rendered = render(content, { reactStrictMode });\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\", \"useObservation\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\n\t\t\t\t\t\t\tconst button =\n\t\t\t\t\t\t\t\trendered.baseElement.querySelector(\"button\") ??\n\t\t\t\t\t\t\t\tassert.fail(\"button not found\");\n\t\t\t\t\t\t\tbutton.click();\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"click\", \"invalidated\"]);\n\n\t\t\t\t\t\t\trendered.rerender(content);\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\n\t\t\t\t\t\t\t\t\"render\",\n\t\t\t\t\t\t\t\t...(reactStrictMode && useObservationHook !== useObservationWithEffects\n\t\t\t\t\t\t\t\t\t? [\"unsubscribe\"]\n\t\t\t\t\t\t\t\t\t: []),\n\t\t\t\t\t\t\t\t\"useObservation\",\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// This requires waiting for finalizers.\n\t\t\t\t\t\t// Forcing two async GCs seems to work robustly, so this is enabled, but if it becomes flakey, it can be tweaked and/or skipped.\n\t\t\t\t\t\tit(`unsubscribe on unmount`, async () => {\n\t\t\t\t\t\t\tassert(global.gc);\n\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tlog.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tfunction TestComponent(this: unknown): JSX.Element {\n\t\t\t\t\t\t\t\treturn useObservationHook((invalidate) => ({\n\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tassertLogEmpty(log);\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\t// Unsubscribe on unmount is done via FinalizationRegistry, so force a GC and wait for it.\n\t\t\t\t\t\t\t// For this to pass on NodeJs experimentally is has been found that this must either do:\n\t\t\t\t\t\t\t// 1. a sync GC then a wait of 8 seconds (but this sometimes fails after multiple runs unless a debugger takes a heap snapshot, possible due to some JIT optimization that breaks it).\n\t\t\t\t\t\t\t// 2. two async GCs in a row.\n\t\t\t\t\t\t\t// Since the second option is both more robust and faster, that is what is used here.\n\t\t\t\t\t\t\tfor (let index = 0; index < 2; index++) {\n\t\t\t\t\t\t\t\tawait global.gc({ type: \"major\", execution: \"async\" });\n\t\t\t\t\t\t\t\tif (log.length > 0) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"unsubscribe\"]);\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tit(\"invalidate after unmount\", () => {\n\t\t\t\t\t\t\tconst log: string[] = [];\n\n\t\t\t\t\t\t\tlet logUnsubscribe = true;\n\n\t\t\t\t\t\t\tconst unsubscribe = (): void => {\n\t\t\t\t\t\t\t\tif (logUnsubscribe) log.push(\"unsubscribe\");\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\tconst invalidateCallbacks: (() => void)[] = [];\n\n\t\t\t\t\t\t\tfunction TestComponent(): JSX.Element {\n\t\t\t\t\t\t\t\tlog.push(\"render\");\n\t\t\t\t\t\t\t\treturn useObservationHook(\n\t\t\t\t\t\t\t\t\t(invalidate) => {\n\t\t\t\t\t\t\t\t\t\tinvalidateCallbacks.push(invalidate);\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\tresult: <br />,\n\t\t\t\t\t\t\t\t\t\t\tunsubscribe,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{ onInvalidation: () => log.push(\"invalidated\") },\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst rendered = render(<TestComponent />, { reactStrictMode });\n\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"render\"]);\n\n\t\t\t\t\t\t\t// After unmount, unsubscribe could happen at any time due to finalizer,so suppress logging it to prevent the test from possibly becoming flaky.\n\t\t\t\t\t\t\tlogUnsubscribe = false;\n\n\t\t\t\t\t\t\trendered.unmount();\n\n\t\t\t\t\t\t\tassert(invalidateCallbacks.length === (reactStrictMode ? 2 : 1));\n\n\t\t\t\t\t\t\t// Invalidate after unmount.\n\t\t\t\t\t\t\t// Since this can happen in real use, due to unsubscribe delay while waiting for finalizer, ensure it does not cause issues.\n\t\t\t\t\t\t\t// This should be a no-op, but since it does a React SetState after unmount, React could object to it.\n\t\t\t\t\t\t\tfor (const callback of invalidateCallbacks) {\n\t\t\t\t\t\t\t\tcallback();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Confirm the invalidation happened..\n\t\t\t\t\t\t\t// If we didn't suppress unsubscribe logging, and the finalizer had run, this could fail (which is why we suppress it).\n\t\t\t\t\t\t\tcheckRenderLog(log, [\"invalidated\"]);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n});\n\n/**\n * Assert that an array is empty.\n *\n * @remarks\n * Not inlined because doing so causes TypeScript to infer the array type as never[] afterwards and breaks push.\n * Better than asserting length is 0 as this gets a better error message on failure.\n */\nfunction assertLogEmpty(log: string[]): void {\n\tassert.deepEqual(log, []);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/react",
3
- "version": "2.70.0-361788",
3
+ "version": "2.71.0",
4
4
  "description": "Utilities for integrating content powered by the Fluid Framework into React applications",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -79,24 +79,24 @@
79
79
  "temp-directory": "nyc/.nyc_output"
80
80
  },
81
81
  "dependencies": {
82
- "@fluidframework/aqueduct": "2.70.0-361788",
83
- "@fluidframework/core-interfaces": "2.70.0-361788",
84
- "@fluidframework/datastore-definitions": "2.70.0-361788",
85
- "@fluidframework/fluid-static": "2.70.0-361788",
86
- "@fluidframework/runtime-definitions": "2.70.0-361788",
87
- "@fluidframework/shared-object-base": "2.70.0-361788",
88
- "@fluidframework/tree": "2.70.0-361788",
82
+ "@fluidframework/aqueduct": "~2.71.0",
83
+ "@fluidframework/core-interfaces": "~2.71.0",
84
+ "@fluidframework/datastore-definitions": "~2.71.0",
85
+ "@fluidframework/fluid-static": "~2.71.0",
86
+ "@fluidframework/runtime-definitions": "~2.71.0",
87
+ "@fluidframework/shared-object-base": "~2.71.0",
88
+ "@fluidframework/tree": "~2.71.0",
89
89
  "react": "^18.3.1"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@arethetypeswrong/cli": "^0.17.1",
93
93
  "@biomejs/biome": "~1.9.3",
94
- "@fluid-internal/mocha-test-setup": "2.70.0-361788",
94
+ "@fluid-internal/mocha-test-setup": "~2.71.0",
95
95
  "@fluid-tools/build-cli": "^0.58.3",
96
96
  "@fluidframework/build-common": "^2.0.3",
97
97
  "@fluidframework/build-tools": "^0.58.3",
98
- "@fluidframework/eslint-config-fluid": "^6.1.0",
99
- "@fluidframework/tinylicious-client": "2.70.0-361788",
98
+ "@fluidframework/eslint-config-fluid": "^7.0.0",
99
+ "@fluidframework/tinylicious-client": "~2.71.0",
100
100
  "@microsoft/api-extractor": "7.52.11",
101
101
  "@testing-library/react": "^16.3.0",
102
102
  "@types/mocha": "^10.0.10",
@@ -142,8 +142,8 @@
142
142
  "check:format": "npm run check:biome",
143
143
  "ci:build:docs": "api-extractor run",
144
144
  "clean": "rimraf --glob dist lib {alpha,beta,internal,legacy}.d.ts \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc",
145
- "eslint": "eslint --format stylish src",
146
- "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
145
+ "eslint": "eslint --quiet --format stylish src",
146
+ "eslint:fix": "eslint --quiet --format stylish src --fix --fix-type problem,suggestion,layout",
147
147
  "format": "npm run format:biome",
148
148
  "format:biome": "biome check . --write",
149
149
  "lint": "fluid-build . --task lint",
Binary file
package/src/propNode.ts CHANGED
@@ -16,7 +16,7 @@ import type { TreeNode, TreeLeafValue } from "@fluidframework/tree";
16
16
  * To convert a TreeNode to this type use {@link toPropTreeNode} or {@link toPropTreeRecord}.
17
17
  * @alpha
18
18
  */
19
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
19
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type
20
20
  export interface PropTreeNode<T extends TreeNode> extends ErasedType<[T, "PropTreeNode"]> {}
21
21
 
22
22
  /**